GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../vi/vs_split.c Lines: 0 208 0.0 %
Date: 2017-11-07 Branches: 0 147 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: vs_split.c,v 1.16 2016/05/27 09:18:12 martijn Exp $	*/
2
3
/*-
4
 * Copyright (c) 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 * Copyright (c) 1993, 1994, 1995, 1996
7
 *	Keith Bostic.  All rights reserved.
8
 *
9
 * See the LICENSE file for redistribution information.
10
 */
11
12
#include "config.h"
13
14
#include <sys/types.h>
15
#include <sys/queue.h>
16
#include <sys/time.h>
17
18
#include <bitstring.h>
19
#include <errno.h>
20
#include <limits.h>
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "../common/common.h"
26
#include "vi.h"
27
28
static SCR *vs_getbg(SCR *, char *);
29
30
/*
31
 * vs_split --
32
 *	Create a new screen.
33
 *
34
 * PUBLIC: int vs_split(SCR *, SCR *, int);
35
 */
36
int
37
vs_split(SCR *sp, SCR *new, int ccl)
38
{
39
	GS *gp;
40
	SMAP *smp;
41
	size_t half;
42
	int issmallscreen, splitup;
43
44
	gp = sp->gp;
45
46
	/* Check to see if it's possible. */
47
	/* XXX: The IS_ONELINE fix will change this, too. */
48
	if (sp->rows < 4) {
49
		msgq(sp, M_ERR,
50
		    "Screen must be larger than %d lines to split", 4 - 1);
51
		return (1);
52
	}
53
54
	/* Wait for any messages in the screen. */
55
	vs_resolve(sp, NULL, 1);
56
57
	half = sp->rows / 2;
58
	if (ccl && half > 6)
59
		half = 6;
60
61
	/* Get a new screen map. */
62
	CALLOC(sp, _HMAP(new), SIZE_HMAP(sp), sizeof(SMAP));
63
	if (_HMAP(new) == NULL)
64
		return (1);
65
	_HMAP(new)->lno = sp->lno;
66
	_HMAP(new)->coff = 0;
67
	_HMAP(new)->soff = 1;
68
69
	/*
70
	 * Small screens: see vs_refresh.c section 6a.  Set a flag so
71
	 * we know to fix the screen up later.
72
	 */
73
	issmallscreen = IS_SMALL(sp);
74
75
	/* The columns in the screen don't change. */
76
	new->cols = sp->cols;
77
78
	/*
79
	 * Split the screen, and link the screens together.  If creating a
80
	 * screen to edit the colon command line or the cursor is in the top
81
	 * half of the current screen, the new screen goes under the current
82
	 * screen.  Else, it goes above the current screen.
83
	 *
84
	 * Recalculate current cursor position based on sp->lno, we're called
85
	 * with the cursor on the colon command line.  Then split the screen
86
	 * in half and update the shared information.
87
	 */
88
	splitup =
89
	    !ccl && (vs_sm_cursor(sp, &smp) ? 0 : (smp - HMAP) + 1) >= half;
90
	if (splitup) {				/* Old is bottom half. */
91
		new->rows = sp->rows - half;	/* New. */
92
		new->woff = sp->woff;
93
		sp->rows = half;		/* Old. */
94
		sp->woff += new->rows;
95
						/* Link in before old. */
96
		TAILQ_INSERT_BEFORE(sp, new, q);
97
98
		/*
99
		 * If the parent is the bottom half of the screen, shift
100
		 * the map down to match on-screen text.
101
		 */
102
		memmove(_HMAP(sp), _HMAP(sp) + new->rows,
103
		    (sp->t_maxrows - new->rows) * sizeof(SMAP));
104
	} else {				/* Old is top half. */
105
		new->rows = half;		/* New. */
106
		sp->rows -= half;		/* Old. */
107
		new->woff = sp->woff + sp->rows;
108
						/* Link in after old. */
109
		TAILQ_INSERT_AFTER(&gp->dq, sp, new, q);
110
	}
111
112
	/* Adjust maximum text count. */
113
	sp->t_maxrows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
114
	new->t_maxrows = IS_ONELINE(new) ? 1 : new->rows - 1;
115
116
	/*
117
	 * Small screens: see vs_refresh.c, section 6a.
118
	 *
119
	 * The child may have different screen options sizes than the parent,
120
	 * so use them.  Guarantee that text counts aren't larger than the
121
	 * new screen sizes.
122
	 */
123
	if (issmallscreen) {
124
		/* Fix the text line count for the parent. */
125
		if (splitup)
126
			sp->t_rows -= new->rows;
127
128
		/* Fix the parent screen. */
129
		if (sp->t_rows > sp->t_maxrows)
130
			sp->t_rows = sp->t_maxrows;
131
		if (sp->t_minrows > sp->t_maxrows)
132
			sp->t_minrows = sp->t_maxrows;
133
134
		/* Fix the child screen. */
135
		new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
136
		if (new->t_rows > new->t_maxrows)
137
			new->t_rows = new->t_maxrows;
138
		if (new->t_minrows > new->t_maxrows)
139
			new->t_minrows = new->t_maxrows;
140
	} else {
141
		sp->t_minrows = sp->t_rows = IS_ONELINE(sp) ? 1 : sp->rows - 1;
142
143
		/*
144
		 * The new screen may be a small screen, even if the parent
145
		 * was not.  Don't complain if O_WINDOW is too large, we're
146
		 * splitting the screen so the screen is much smaller than
147
		 * normal.
148
		 */
149
		new->t_minrows = new->t_rows = O_VAL(sp, O_WINDOW);
150
		if (new->t_rows > new->rows - 1)
151
			new->t_minrows = new->t_rows =
152
			    IS_ONELINE(new) ? 1 : new->rows - 1;
153
	}
154
155
	/* Adjust the ends of the new and old maps. */
156
	_TMAP(sp) = IS_ONELINE(sp) ?
157
	    _HMAP(sp) : _HMAP(sp) + (sp->t_rows - 1);
158
	_TMAP(new) = IS_ONELINE(new) ?
159
	    _HMAP(new) : _HMAP(new) + (new->t_rows - 1);
160
161
	/* Reset the length of the default scroll. */
162
	if ((sp->defscroll = sp->t_maxrows / 2) == 0)
163
		sp->defscroll = 1;
164
	if ((new->defscroll = new->t_maxrows / 2) == 0)
165
		new->defscroll = 1;
166
167
	/*
168
	 * Initialize the screen flags:
169
	 *
170
	 * If we're in vi mode in one screen, we don't have to reinitialize.
171
	 * This isn't just a cosmetic fix.  The path goes like this:
172
	 *
173
	 *	return into vi(), SC_SSWITCH set
174
	 *	call vs_refresh() with SC_STATUS set
175
	 *	call vs_resolve to display the status message
176
	 *	call vs_refresh() because the SC_SCR_VI bit isn't set
177
	 *
178
	 * Things go downhill at this point.
179
	 *
180
	 * Draw the new screen from scratch, and add a status line.
181
	 */
182
	F_SET(new,
183
	    SC_SCR_REFORMAT | SC_STATUS |
184
	    F_ISSET(sp, SC_EX | SC_VI | SC_SCR_VI | SC_SCR_EX));
185
	return (0);
186
}
187
188
/*
189
 * vs_discard --
190
 *	Discard the screen, folding the real-estate into a related screen,
191
 *	if one exists, and return that screen.
192
 *
193
 * PUBLIC: int vs_discard(SCR *, SCR **);
194
 */
195
int
196
vs_discard(SCR *sp, SCR **spp)
197
{
198
	SCR *nsp;
199
	dir_t dir;
200
201
	/*
202
	 * Save the old screen's cursor information.
203
	 *
204
	 * XXX
205
	 * If called after file_end(), and the underlying file was a tmp
206
	 * file, it may have gone away.
207
	 */
208
	if (sp->frp != NULL) {
209
		sp->frp->lno = sp->lno;
210
		sp->frp->cno = sp->cno;
211
		F_SET(sp->frp, FR_CURSORSET);
212
	}
213
214
	/*
215
	 * Add into a previous screen and then into a subsequent screen, as
216
	 * they're the closest to the current screen.  If that doesn't work,
217
	 * there was no screen to join.
218
	 */
219
	if ((nsp = TAILQ_PREV(sp, _dqh, q))) {
220
		nsp->rows += sp->rows;
221
		sp = nsp;
222
		dir = FORWARD;
223
	} else if ((nsp = TAILQ_NEXT(sp, q))) {
224
		nsp->woff = sp->woff;
225
		nsp->rows += sp->rows;
226
		sp = nsp;
227
		dir = BACKWARD;
228
	} else {
229
		sp = NULL;
230
		dir = 0;	/* unused */
231
	}
232
233
	if (spp != NULL)
234
		*spp = sp;
235
	if (sp == NULL)
236
		return (0);
237
238
	/*
239
	 * Make no effort to clean up the discarded screen's information.  If
240
	 * it's not exiting, we'll do the work when the user redisplays it.
241
	 *
242
	 * Small screens: see vs_refresh.c section 6a.  Adjust text line info,
243
	 * unless it's a small screen.
244
	 *
245
	 * Reset the length of the default scroll.
246
	 */
247
	if (!IS_SMALL(sp))
248
		sp->t_rows = sp->t_minrows = sp->rows - 1;
249
	sp->t_maxrows = sp->rows - 1;
250
	sp->defscroll = sp->t_maxrows / 2;
251
	*(HMAP + (sp->t_rows - 1)) = *TMAP;
252
	TMAP = HMAP + (sp->t_rows - 1);
253
254
	/*
255
	 * Draw the new screen from scratch, and add a status line.
256
	 *
257
	 * XXX
258
	 * We could play games with the map, if this were ever to be a
259
	 * performance problem, but I wrote the code a few times and it
260
	 * was never clean or easy.
261
	 */
262
	switch (dir) {
263
	case FORWARD:
264
		vs_sm_fill(sp, OOBLNO, P_TOP);
265
		break;
266
	case BACKWARD:
267
		vs_sm_fill(sp, OOBLNO, P_BOTTOM);
268
		break;
269
	default:
270
		abort();
271
	}
272
273
	F_SET(sp, SC_STATUS);
274
	return (0);
275
}
276
277
/*
278
 * vs_fg --
279
 *	Background the current screen, and foreground a new one.
280
 *
281
 * PUBLIC: int vs_fg(SCR *, SCR **, CHAR_T *, int);
282
 */
283
int
284
vs_fg(SCR *sp, SCR **nspp, CHAR_T *name, int newscreen)
285
{
286
	GS *gp;
287
	SCR *nsp;
288
289
	gp = sp->gp;
290
291
	if (newscreen)
292
		/* Get the specified background screen. */
293
		nsp = vs_getbg(sp, name);
294
	else
295
		/* Swap screens. */
296
		if (vs_swap(sp, &nsp, name))
297
			return (1);
298
299
	if ((*nspp = nsp) == NULL) {
300
		msgq_str(sp, M_ERR, name,
301
		    name == NULL ?
302
		    "There are no background screens" :
303
		    "There's no background screen editing a file named %s");
304
		return (1);
305
	}
306
307
	if (newscreen) {
308
		/* Remove the new screen from the background queue. */
309
		TAILQ_REMOVE(&gp->hq, nsp, q);
310
311
		/* Split the screen; if we fail, hook the screen back in. */
312
		if (vs_split(sp, nsp, 0)) {
313
			TAILQ_INSERT_TAIL(&gp->hq, nsp, q);
314
			return (1);
315
		}
316
	} else {
317
		/* Move the old screen to the background queue. */
318
		TAILQ_REMOVE(&gp->dq, sp, q);
319
		TAILQ_INSERT_TAIL(&gp->hq, sp, q);
320
	}
321
	return (0);
322
}
323
324
/*
325
 * vs_bg --
326
 *	Background the screen, and switch to the next one.
327
 *
328
 * PUBLIC: int vs_bg(SCR *);
329
 */
330
int
331
vs_bg(SCR *sp)
332
{
333
	GS *gp;
334
	SCR *nsp;
335
336
	gp = sp->gp;
337
338
	/* Try and join with another screen. */
339
	if (vs_discard(sp, &nsp))
340
		return (1);
341
	if (nsp == NULL) {
342
		msgq(sp, M_ERR,
343
		    "You may not background your only displayed screen");
344
		return (1);
345
	}
346
347
	/* Move the old screen to the background queue. */
348
	TAILQ_REMOVE(&gp->dq, sp, q);
349
	TAILQ_INSERT_TAIL(&gp->hq, sp, q);
350
351
	/* Toss the screen map. */
352
	free(_HMAP(sp));
353
	_HMAP(sp) = NULL;
354
355
	/* Switch screens. */
356
	sp->nextdisp = nsp;
357
	F_SET(sp, SC_SSWITCH);
358
359
	return (0);
360
}
361
362
/*
363
 * vs_swap --
364
 *	Swap the current screen with a backgrounded one.
365
 *
366
 * PUBLIC: int vs_swap(SCR *, SCR **, char *);
367
 */
368
int
369
vs_swap(SCR *sp, SCR **nspp, char *name)
370
{
371
	GS *gp;
372
	SCR *nsp;
373
374
	gp = sp->gp;
375
376
	/* Get the specified background screen. */
377
	if ((*nspp = nsp = vs_getbg(sp, name)) == NULL)
378
		return (0);
379
380
	/*
381
	 * Save the old screen's cursor information.
382
	 *
383
	 * XXX
384
	 * If called after file_end(), and the underlying file was a tmp
385
	 * file, it may have gone away.
386
	 */
387
	if (sp->frp != NULL) {
388
		sp->frp->lno = sp->lno;
389
		sp->frp->cno = sp->cno;
390
		F_SET(sp->frp, FR_CURSORSET);
391
	}
392
393
	/* Switch screens. */
394
	sp->nextdisp = nsp;
395
	F_SET(sp, SC_SSWITCH);
396
397
	/* Initialize terminal information. */
398
	VIP(nsp)->srows = VIP(sp)->srows;
399
400
	/* Initialize screen information. */
401
	nsp->cols = sp->cols;
402
	nsp->rows = sp->rows;	/* XXX: Only place in vi that sets rows. */
403
	nsp->woff = sp->woff;
404
405
	/*
406
	 * Small screens: see vs_refresh.c, section 6a.
407
	 *
408
	 * The new screens may have different screen options sizes than the
409
	 * old one, so use them.  Make sure that text counts aren't larger
410
	 * than the new screen sizes.
411
	 */
412
	if (IS_SMALL(nsp)) {
413
		nsp->t_minrows = nsp->t_rows = O_VAL(nsp, O_WINDOW);
414
		if (nsp->t_rows > sp->t_maxrows)
415
			nsp->t_rows = nsp->t_maxrows;
416
		if (nsp->t_minrows > sp->t_maxrows)
417
			nsp->t_minrows = nsp->t_maxrows;
418
	} else
419
		nsp->t_rows = nsp->t_maxrows = nsp->t_minrows = nsp->rows - 1;
420
421
	/* Reset the length of the default scroll. */
422
	nsp->defscroll = nsp->t_maxrows / 2;
423
424
	/* Allocate a new screen map. */
425
	CALLOC_RET(nsp, _HMAP(nsp), SIZE_HMAP(nsp), sizeof(SMAP));
426
	_TMAP(nsp) = _HMAP(nsp) + (nsp->t_rows - 1);
427
428
	/* Fill the map. */
429
	if (vs_sm_fill(nsp, nsp->lno, P_FILL))
430
		return (1);
431
432
	/*
433
	 * The new screen replaces the old screen in the parent/child list.
434
	 * We insert the new screen after the old one.  If we're exiting,
435
	 * the exit will delete the old one, if we're foregrounding, the fg
436
	 * code will move the old one to the background queue.
437
	 */
438
	TAILQ_REMOVE(&gp->hq, nsp, q);
439
	TAILQ_INSERT_AFTER(&gp->dq, sp, nsp, q);
440
441
	/*
442
	 * Don't change the screen's cursor information other than to
443
	 * note that the cursor is wrong.
444
	 */
445
	F_SET(VIP(nsp), VIP_CUR_INVALID);
446
447
	/* Draw the new screen from scratch, and add a status line. */
448
	F_SET(nsp, SC_SCR_REDRAW | SC_STATUS);
449
	return (0);
450
}
451
452
/*
453
 * vs_resize --
454
 *	Change the absolute size of the current screen.
455
 *
456
 * PUBLIC: int vs_resize(SCR *, long, adj_t);
457
 */
458
int
459
vs_resize(SCR *sp, long count, adj_t adj)
460
{
461
	GS *gp;
462
	SCR *g, *s;
463
	size_t g_off, s_off;
464
465
	gp = sp->gp;
466
467
	/*
468
	 * Figure out which screens will grow, which will shrink, and
469
	 * make sure it's possible.
470
	 */
471
	if (count == 0)
472
		return (0);
473
	if (adj == A_SET) {
474
		if (sp->t_maxrows == count)
475
			return (0);
476
		if (sp->t_maxrows > count) {
477
			adj = A_DECREASE;
478
			count = sp->t_maxrows - count;
479
		} else {
480
			adj = A_INCREASE;
481
			count = count - sp->t_maxrows;
482
		}
483
	}
484
485
	g_off = s_off = 0;
486
	if (adj == A_DECREASE) {
487
		if (count < 0)
488
			count = -count;
489
		s = sp;
490
		if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
491
			goto toosmall;
492
		if ((g = TAILQ_PREV(sp, _dqh, q)) == NULL) {
493
			if ((g = TAILQ_NEXT(sp, q)) == NULL)
494
				goto toobig;
495
			g_off = -count;
496
		} else
497
			s_off = count;
498
	} else {
499
		g = sp;
500
		if ((s = TAILQ_NEXT(sp, q)))
501
			if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
502
				s = NULL;
503
			else
504
				s_off = count;
505
		else
506
			s = NULL;
507
		if (s == NULL) {
508
			if ((s = TAILQ_PREV(sp, _dqh, q)) == NULL) {
509
toobig:				msgq(sp, M_BERR, adj == A_DECREASE ?
510
				    "The screen cannot shrink" :
511
				    "The screen cannot grow");
512
				return (1);
513
			}
514
			if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
515
toosmall:			msgq(sp, M_BERR,
516
				    "The screen can only shrink to %d rows",
517
				    MINIMUM_SCREEN_ROWS);
518
				return (1);
519
			}
520
			g_off = -count;
521
		}
522
	}
523
524
	/*
525
	 * Fix up the screens; we could optimize the reformatting of the
526
	 * screen, but this isn't likely to be a common enough operation
527
	 * to make it worthwhile.
528
	 */
529
	s->rows += -count;
530
	s->woff += s_off;
531
	g->rows += count;
532
	g->woff += g_off;
533
534
	g->t_rows += count;
535
	if (g->t_minrows == g->t_maxrows)
536
		g->t_minrows += count;
537
	g->t_maxrows += count;
538
	_TMAP(g) += count;
539
	F_SET(g, SC_SCR_REFORMAT | SC_STATUS);
540
541
	s->t_rows -= count;
542
	s->t_maxrows -= count;
543
	if (s->t_minrows > s->t_maxrows)
544
		s->t_minrows = s->t_maxrows;
545
	_TMAP(s) -= count;
546
	F_SET(s, SC_SCR_REFORMAT | SC_STATUS);
547
548
	return (0);
549
}
550
551
/*
552
 * vs_getbg --
553
 *	Get the specified background screen, or, if name is NULL, the first
554
 *	background screen.
555
 */
556
static SCR *
557
vs_getbg(SCR *sp, char *name)
558
{
559
	GS *gp;
560
	SCR *nsp;
561
	char *p;
562
563
	gp = sp->gp;
564
565
	/* If name is NULL, return the first background screen on the list. */
566
	if (name == NULL)
567
		return (TAILQ_FIRST(&gp->hq));
568
569
	/* Search for a full match. */
570
	TAILQ_FOREACH(nsp, &gp->hq, q) {
571
		if (!strcmp(nsp->frp->name, name))
572
			return(nsp);
573
	}
574
575
	/* Search for a last-component match. */
576
	TAILQ_FOREACH(nsp, &gp->hq, q) {
577
		if ((p = strrchr(nsp->frp->name, '/')) == NULL)
578
			p = nsp->frp->name;
579
		else
580
			++p;
581
		if (!strcmp(p, name))
582
			return(nsp);
583
	}
584
585
	return (NULL);
586
}