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