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