1/*-
2 * Copyright (c) 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 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 <ctype.h>
18#include <limits.h>
19#include <stdarg.h>
20#include <stdio.h>
21#include <string.h>
22
23#include "../common/common.h"
24
25static int ex_prchars(SCR *,
26    const CHAR_T *, size_t *, size_t, u_int, int);
27
28/*
29 * ex_list -- :[line [,line]] l[ist] [count] [flags]
30 *
31 *	Display the addressed lines such that the output is unambiguous.
32 *
33 * PUBLIC: int ex_list(SCR *, EXCMD *);
34 */
35int
36ex_list(SCR *sp, EXCMD *cmdp)
37{
38	if (ex_print(sp, cmdp,
39	    &cmdp->addr1, &cmdp->addr2, cmdp->iflags | E_C_LIST))
40		return (1);
41	sp->lno = cmdp->addr2.lno;
42	sp->cno = cmdp->addr2.cno;
43	return (0);
44}
45
46/*
47 * ex_number -- :[line [,line]] nu[mber] [count] [flags]
48 *
49 *	Display the addressed lines with a leading line number.
50 *
51 * PUBLIC: int ex_number(SCR *, EXCMD *);
52 */
53int
54ex_number(SCR *sp, EXCMD *cmdp)
55{
56	if (ex_print(sp, cmdp,
57	    &cmdp->addr1, &cmdp->addr2, cmdp->iflags | E_C_HASH))
58		return (1);
59	sp->lno = cmdp->addr2.lno;
60	sp->cno = cmdp->addr2.cno;
61	return (0);
62}
63
64/*
65 * ex_pr -- :[line [,line]] p[rint] [count] [flags]
66 *
67 *	Display the addressed lines.
68 *
69 * PUBLIC: int ex_pr(SCR *, EXCMD *);
70 */
71int
72ex_pr(SCR *sp, EXCMD *cmdp)
73{
74	if (ex_print(sp, cmdp, &cmdp->addr1, &cmdp->addr2, cmdp->iflags))
75		return (1);
76	sp->lno = cmdp->addr2.lno;
77	sp->cno = cmdp->addr2.cno;
78	return (0);
79}
80
81/*
82 * ex_print --
83 *	Print the selected lines.
84 *
85 * PUBLIC: int ex_print(SCR *, EXCMD *, MARK *, MARK *, u_int32_t);
86 */
87int
88ex_print(SCR *sp, EXCMD *cmdp, MARK *fp, MARK *tp, u_int32_t flags)
89{
90	GS *gp;
91	recno_t from, to;
92	size_t col, len;
93	CHAR_T *p;
94	CHAR_T buf[10];
95
96	NEEDFILE(sp, cmdp);
97
98	gp = sp->gp;
99	for (from = fp->lno, to = tp->lno; from <= to; ++from) {
100		col = 0;
101
102		/*
103		 * Display the line number.  The %6 format is specified
104		 * by POSIX 1003.2, and is almost certainly large enough.
105		 * Check, though, just in case.
106		 */
107		if (LF_ISSET(E_C_HASH)) {
108			if (from <= 999999) {
109				SPRINTF(buf, SIZE(buf), L("%6u  "), from);
110				p = buf;
111			} else
112				p = L("TOOBIG  ");
113			if (ex_prchars(sp, p, &col, 8, 0, 0))
114				return (1);
115		}
116
117		/*
118		 * Display the line.  The format for E_C_PRINT isn't very good,
119		 * especially in handling end-of-line tabs, but they're almost
120		 * backward compatible.
121		 */
122		if (db_get(sp, from, DBG_FATAL, &p, &len))
123			return (1);
124
125		if (len == 0 && !LF_ISSET(E_C_LIST))
126			(void)ex_puts(sp, "\n");
127		else if (ex_ldisplay(sp, p, len, col, flags))
128			return (1);
129
130		if (INTERRUPTED(sp))
131			break;
132	}
133	return (0);
134}
135
136/*
137 * ex_ldisplay --
138 *	Display a line without any preceding number.
139 *
140 * PUBLIC: int ex_ldisplay(SCR *, const CHAR_T *, size_t, size_t, u_int);
141 */
142int
143ex_ldisplay(SCR *sp, const CHAR_T *p, size_t len, size_t col, u_int flags)
144{
145	if (len > 0 && ex_prchars(sp, p, &col, len, LF_ISSET(E_C_LIST), 0))
146		return (1);
147	if (!INTERRUPTED(sp) && LF_ISSET(E_C_LIST)) {
148		p = L("$");
149		if (ex_prchars(sp, p, &col, 1, LF_ISSET(E_C_LIST), 0))
150			return (1);
151	}
152	if (!INTERRUPTED(sp))
153		(void)ex_puts(sp, "\n");
154	return (0);
155}
156
157/*
158 * ex_scprint --
159 *	Display a line for the substitute with confirmation routine.
160 *
161 * PUBLIC: int ex_scprint(SCR *, MARK *, MARK *);
162 */
163int
164ex_scprint(SCR *sp, MARK *fp, MARK *tp)
165{
166	CHAR_T *p;
167	size_t col, len;
168
169	col = 0;
170	if (O_ISSET(sp, O_NUMBER)) {
171		p = L("        ");
172		if (ex_prchars(sp, p, &col, 8, 0, 0))
173			return (1);
174	}
175
176	if (db_get(sp, fp->lno, DBG_FATAL, &p, &len))
177		return (1);
178
179	if (ex_prchars(sp, p, &col, fp->cno, 0, ' '))
180		return (1);
181	p += fp->cno;
182	if (ex_prchars(sp,
183	    p, &col, tp->cno == fp->cno ? 1 : tp->cno - fp->cno, 0, '^'))
184		return (1);
185	if (INTERRUPTED(sp))
186		return (1);
187	p = L("[ynq]");		/* XXX: should be msg_cat. */
188	if (ex_prchars(sp, p, &col, 5, 0, 0))
189		return (1);
190	(void)ex_fflush(sp);
191	return (0);
192}
193
194/*
195 * ex_prchars --
196 *	Local routine to dump characters to the screen.
197 */
198static int
199ex_prchars(SCR *sp, const CHAR_T *p, size_t *colp, size_t len,
200	    u_int flags, int repeatc)
201{
202	CHAR_T ch;
203	char *kp;
204	GS *gp;
205	size_t col, tlen, ts;
206
207	if (O_ISSET(sp, O_LIST))
208		LF_SET(E_C_LIST);
209	gp = sp->gp;
210	ts = O_VAL(sp, O_TABSTOP);
211	for (col = *colp; len--;)
212		if ((ch = *p++) == '\t' && !LF_ISSET(E_C_LIST))
213			for (tlen = ts - col % ts;
214			    col < sp->cols && tlen--; ++col) {
215				(void)ex_printf(sp,
216				    "%c", repeatc ? repeatc : ' ');
217				if (INTERRUPTED(sp))
218					goto intr;
219			}
220		else {
221			kp = KEY_NAME(sp, ch);
222			tlen = KEY_COL(sp, ch);
223
224			/*
225			 * Start a new line if the last character does not fit
226			 * into the current line.  The implicit new lines are
227			 * not interruptible.
228			 */
229			if (col + tlen > sp->cols) {
230				col = 0;
231				(void)ex_puts(sp, "\n");
232			}
233
234			col += tlen;
235			if (!repeatc) {
236				(void)ex_puts(sp, kp);
237				if (INTERRUPTED(sp))
238					goto intr;
239			} else while (tlen--) {
240				(void)ex_printf(sp, "%c", repeatc);
241				if (INTERRUPTED(sp))
242					goto intr;
243			}
244			if (col == sp->cols) {
245				col = 0;
246				(void)ex_puts(sp, "\n");
247			}
248		}
249intr:	*colp = col;
250	return (0);
251}
252
253/*
254 * ex_printf --
255 *	Ex's version of printf.
256 *
257 * PUBLIC: int ex_printf(SCR *, const char *, ...);
258 */
259int
260ex_printf(
261	SCR *sp,
262	const char *fmt,
263	...)
264{
265	EX_PRIVATE *exp;
266	va_list ap;
267	size_t n;
268
269	exp = EXP(sp);
270
271	va_start(ap, fmt);
272	exp->obp_len += n = vsnprintf(exp->obp + exp->obp_len,
273	    sizeof(exp->obp) - exp->obp_len, fmt, ap);
274	va_end(ap);
275
276	/* Flush when reach a <newline> or half the buffer. */
277	if (exp->obp[exp->obp_len - 1] == '\n' ||
278	    exp->obp_len > sizeof(exp->obp) / 2)
279		(void)ex_fflush(sp);
280	return (n);
281}
282
283/*
284 * ex_puts --
285 *	Ex's version of puts.
286 *
287 * PUBLIC: int ex_puts(SCR *, const char *);
288 */
289int
290ex_puts(SCR *sp, const char *str)
291{
292	EX_PRIVATE *exp;
293	int doflush, n;
294
295	exp = EXP(sp);
296
297	/* Flush when reach a <newline> or the end of the buffer. */
298	for (doflush = n = 0; *str != '\0'; ++n) {
299		if (exp->obp_len > sizeof(exp->obp))
300			(void)ex_fflush(sp);
301		if ((exp->obp[exp->obp_len++] = *str++) == '\n')
302			doflush = 1;
303	}
304	if (doflush)
305		(void)ex_fflush(sp);
306	return (n);
307}
308
309/*
310 * ex_fflush --
311 *	Ex's version of fflush.
312 *
313 * PUBLIC: int ex_fflush(SCR *sp);
314 */
315int
316ex_fflush(SCR *sp)
317{
318	EX_PRIVATE *exp;
319
320	exp = EXP(sp);
321
322	if (exp->obp_len != 0) {
323		sp->gp->scr_msg(sp, M_NONE, exp->obp, exp->obp_len);
324		exp->obp_len = 0;
325	}
326	return (0);
327}
328