1 /* $OpenBSD: ex_print.c,v 1.13 2016/05/27 09:18:12 martijn 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 #include <sys/types.h> 15 #include <sys/queue.h> 16 17 #include <bitstring.h> 18 #include <ctype.h> 19 #include <limits.h> 20 #include <stdarg.h> 21 #include <stdio.h> 22 #include <string.h> 23 24 #include "../common/common.h" 25 26 static int ex_prchars(SCR *, const char *, 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 */ 35 int 36 ex_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 */ 53 int 54 ex_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 */ 71 int 72 ex_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 */ 87 int 88 ex_print(SCR *sp, EXCMD *cmdp, MARK *fp, MARK *tp, u_int32_t flags) 89 { 90 recno_t from, to; 91 size_t col, len; 92 char *p, buf[10]; 93 94 NEEDFILE(sp, cmdp); 95 96 for (from = fp->lno, to = tp->lno; from <= to; ++from) { 97 col = 0; 98 99 /* 100 * Display the line number. The %6 format is specified 101 * by POSIX 1003.2, and is almost certainly large enough. 102 * Check, though, just in case. 103 */ 104 if (LF_ISSET(E_C_HASH)) { 105 if (from <= 999999) { 106 snprintf(buf, sizeof(buf), "%6lu ", (ulong)from); 107 p = buf; 108 } else 109 p = "TOOBIG "; 110 if (ex_prchars(sp, p, &col, 8, 0, 0)) 111 return (1); 112 } 113 114 /* 115 * Display the line. The format for E_C_PRINT isn't very good, 116 * especially in handling end-of-line tabs, but they're almost 117 * backward compatible. 118 */ 119 if (db_get(sp, from, DBG_FATAL, &p, &len)) 120 return (1); 121 122 if (len == 0 && !LF_ISSET(E_C_LIST)) 123 (void)ex_puts(sp, "\n"); 124 else if (ex_ldisplay(sp, p, len, col, flags)) 125 return (1); 126 127 if (INTERRUPTED(sp)) 128 break; 129 } 130 return (0); 131 } 132 133 /* 134 * ex_ldisplay -- 135 * Display a line without any preceding number. 136 * 137 * PUBLIC: int ex_ldisplay(SCR *, const char *, size_t, size_t, u_int); 138 */ 139 int 140 ex_ldisplay(SCR *sp, const char *p, size_t len, size_t col, u_int flags) 141 { 142 if (len > 0 && ex_prchars(sp, p, &col, len, LF_ISSET(E_C_LIST), 0)) 143 return (1); 144 if (!INTERRUPTED(sp) && LF_ISSET(E_C_LIST)) { 145 p = "$"; 146 if (ex_prchars(sp, p, &col, 1, LF_ISSET(E_C_LIST), 0)) 147 return (1); 148 } 149 if (!INTERRUPTED(sp)) 150 (void)ex_puts(sp, "\n"); 151 return (0); 152 } 153 154 /* 155 * ex_scprint -- 156 * Display a line for the substitute with confirmation routine. 157 * 158 * PUBLIC: int ex_scprint(SCR *, MARK *, MARK *); 159 */ 160 int 161 ex_scprint(SCR *sp, MARK *fp, MARK *tp) 162 { 163 const char *p; 164 size_t col, len; 165 166 col = 0; 167 if (O_ISSET(sp, O_NUMBER)) { 168 p = " "; 169 if (ex_prchars(sp, p, &col, 8, 0, 0)) 170 return (1); 171 } 172 173 if (db_get(sp, fp->lno, DBG_FATAL, (char **)&p, &len)) 174 return (1); 175 176 if (ex_prchars(sp, p, &col, fp->cno, 0, ' ')) 177 return (1); 178 p += fp->cno; 179 if (ex_prchars(sp, 180 p, &col, tp->cno == fp->cno ? 1 : tp->cno - fp->cno, 0, '^')) 181 return (1); 182 if (INTERRUPTED(sp)) 183 return (1); 184 p = "[ynq]"; 185 if (ex_prchars(sp, p, &col, 5, 0, 0)) 186 return (1); 187 (void)ex_fflush(sp); 188 return (0); 189 } 190 191 /* 192 * ex_prchars -- 193 * Local routine to dump characters to the screen. 194 */ 195 static int 196 ex_prchars(SCR *sp, const char *p, size_t *colp, size_t len, u_int flags, 197 int repeatc) 198 { 199 CHAR_T ch, *kp; 200 size_t col, tlen, ts; 201 202 if (O_ISSET(sp, O_LIST)) 203 LF_SET(E_C_LIST); 204 ts = O_VAL(sp, O_TABSTOP); 205 for (col = *colp; len--;) 206 if ((ch = *p++) == '\t' && !LF_ISSET(E_C_LIST)) 207 for (tlen = ts - col % ts; 208 col < sp->cols && tlen--; ++col) { 209 (void)ex_printf(sp, 210 "%c", repeatc ? repeatc : ' '); 211 if (INTERRUPTED(sp)) 212 goto intr; 213 } 214 else { 215 kp = KEY_NAME(sp, ch); 216 tlen = KEY_LEN(sp, ch); 217 if (!repeatc && col + tlen < sp->cols) { 218 (void)ex_puts(sp, kp); 219 col += tlen; 220 } else 221 for (; tlen--; ++kp, ++col) { 222 if (col == sp->cols) { 223 col = 0; 224 (void)ex_puts(sp, "\n"); 225 } 226 (void)ex_printf(sp, 227 "%c", repeatc ? repeatc : *kp); 228 if (INTERRUPTED(sp)) 229 goto intr; 230 } 231 } 232 intr: *colp = col; 233 return (0); 234 } 235 236 /* 237 * ex_printf -- 238 * Ex's version of printf. 239 * 240 * PUBLIC: int ex_printf(SCR *, const char *, ...); 241 */ 242 int 243 ex_printf(SCR *sp, const char *fmt, ...) 244 { 245 EX_PRIVATE *exp; 246 va_list ap; 247 size_t n; 248 249 exp = EXP(sp); 250 251 va_start(ap, fmt); 252 n = vsnprintf(exp->obp + exp->obp_len, 253 sizeof(exp->obp) - exp->obp_len, fmt, ap); 254 va_end(ap); 255 if (n >= sizeof(exp->obp) - exp->obp_len) 256 n = sizeof(exp->obp) - exp->obp_len - 1; 257 exp->obp_len += n; 258 259 /* Flush when reach a <newline> or half the buffer. */ 260 if (exp->obp[exp->obp_len - 1] == '\n' || 261 exp->obp_len > sizeof(exp->obp) / 2) 262 (void)ex_fflush(sp); 263 return (n); 264 } 265 266 /* 267 * ex_puts -- 268 * Ex's version of puts. 269 * 270 * PUBLIC: int ex_puts(SCR *, const char *); 271 */ 272 int 273 ex_puts(SCR *sp, const char *str) 274 { 275 EX_PRIVATE *exp; 276 int doflush, n; 277 278 exp = EXP(sp); 279 280 /* Flush when reach a <newline> or the end of the buffer. */ 281 for (doflush = n = 0; *str != '\0'; ++n) { 282 if (exp->obp_len > sizeof(exp->obp)) 283 (void)ex_fflush(sp); 284 if ((exp->obp[exp->obp_len++] = *str++) == '\n') 285 doflush = 1; 286 } 287 if (doflush) 288 (void)ex_fflush(sp); 289 return (n); 290 } 291 292 /* 293 * ex_fflush -- 294 * Ex's version of fflush. 295 * 296 * PUBLIC: int ex_fflush(SCR *sp); 297 */ 298 int 299 ex_fflush(SCR *sp) 300 { 301 EX_PRIVATE *exp; 302 303 exp = EXP(sp); 304 305 if (exp->obp_len != 0) { 306 sp->gp->scr_msg(sp, M_NONE, exp->obp, exp->obp_len); 307 exp->obp_len = 0; 308 } 309 return (0); 310 } 311