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#include <sys/types.h>
13#include <sys/queue.h>
14#include <sys/time.h>
15
16#include <bitstring.h>
17#include <limits.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "../common/common.h"
23
24/*
25 * ex_z -- :[line] z [^-.+=] [count] [flags]
26 *	Adjust window.
27 *
28 * PUBLIC: int ex_z(SCR *, EXCMD *);
29 */
30int
31ex_z(SCR *sp, EXCMD *cmdp)
32{
33	MARK abs;
34	recno_t cnt, equals, lno;
35	int eofcheck;
36
37	NEEDFILE(sp, cmdp);
38
39	/*
40	 * !!!
41	 * If no count specified, use either two times the size of the
42	 * scrolling region, or the size of the window option.  POSIX
43	 * 1003.2 claims that the latter is correct, but historic ex/vi
44	 * documentation and practice appear to use the scrolling region.
45	 * I'm using the window size as it means that the entire screen
46	 * is used instead of losing a line to roundoff.  Note, we drop
47	 * a line from the cnt if using the window size to leave room for
48	 * the next ex prompt.
49	 */
50	if (FL_ISSET(cmdp->iflags, E_C_COUNT))
51		cnt = cmdp->count;
52	else
53		cnt = O_VAL(sp, O_WINDOW) - 1;
54
55	equals = 0;
56	eofcheck = 0;
57	lno = cmdp->addr1.lno;
58
59	switch (FL_ISSET(cmdp->iflags,
60	    E_C_CARAT | E_C_DASH | E_C_DOT | E_C_EQUAL | E_C_PLUS)) {
61	case E_C_CARAT:		/* Display cnt * 2 before the line. */
62		eofcheck = 1;
63		if (lno > cnt * 2)
64			cmdp->addr1.lno = (lno - cnt * 2) + 1;
65		else
66			cmdp->addr1.lno = 1;
67		cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1;
68		break;
69	case E_C_DASH:		/* Line goes at the bottom of the screen. */
70		cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1;
71		cmdp->addr2.lno = lno;
72		break;
73	case E_C_DOT:		/* Line goes in the middle of the screen. */
74		/*
75		 * !!!
76		 * Historically, the "middleness" of the line overrode the
77		 * count, so that "3z.19" or "3z.20" would display the first
78		 * 12 lines of the file, i.e. (N - 1) / 2 lines before and
79		 * after the specified line.
80		 */
81		eofcheck = 1;
82		cnt = (cnt - 1) / 2;
83		cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
84		cmdp->addr2.lno = lno + cnt;
85
86		/*
87		 * !!!
88		 * Historically, z. set the absolute cursor mark.
89		 */
90		abs.lno = sp->lno;
91		abs.cno = sp->cno;
92		(void)mark_set(sp, ABSMARK1, &abs, 1);
93		break;
94	case E_C_EQUAL:		/* Center with hyphens. */
95		/*
96		 * !!!
97		 * Strangeness.  The '=' flag is like the '.' flag (see the
98		 * above comment, it applies here as well) but with a special
99		 * little hack.  Print out lines of hyphens before and after
100		 * the specified line.  Additionally, the cursor remains set
101		 * on that line.
102		 */
103		eofcheck = 1;
104		cnt = (cnt - 1) / 2;
105		cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
106		cmdp->addr2.lno = lno - 1;
107		if (ex_pr(sp, cmdp))
108			return (1);
109		(void)ex_puts(sp, "----------------------------------------\n");
110		cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
111		if (ex_pr(sp, cmdp))
112			return (1);
113		(void)ex_puts(sp, "----------------------------------------\n");
114		cmdp->addr1.lno = lno + 1;
115		cmdp->addr2.lno = (lno + cnt) - 1;
116		break;
117	default:
118		/* If no line specified, move to the next one. */
119		if (F_ISSET(cmdp, E_ADDR_DEF))
120			++lno;
121		/* FALLTHROUGH */
122	case E_C_PLUS:		/* Line goes at the top of the screen. */
123		eofcheck = 1;
124		cmdp->addr1.lno = lno;
125		cmdp->addr2.lno = (lno + cnt) - 1;
126		break;
127	}
128
129	if (eofcheck) {
130		if (db_last(sp, &lno))
131			return (1);
132		if (cmdp->addr2.lno > lno)
133			cmdp->addr2.lno = lno;
134	}
135
136	if (ex_pr(sp, cmdp))
137		return (1);
138	if (equals)
139		sp->lno = equals;
140	return (0);
141}
142