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