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 25 static 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 */ 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 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 */ 142 int 143 ex_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 */ 163 int 164 ex_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 */ 198 static int 199 ex_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 } 249 intr: *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 */ 259 int 260 ex_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 */ 289 int 290 ex_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 */ 315 int 316 ex_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