1 /* 2 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice in the documentation and/or other materials provided with 12 * the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 20 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 21 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 28 /* 29 * Routines to manipulate the "line buffer". 30 * The line buffer holds a line of output as it is being built 31 * in preparation for output to the screen. 32 */ 33 34 #include "less.h" 35 36 public char linebuf[1024]; /* Buffer which holds the current output line */ 37 public int size_linebuf = sizeof(linebuf); 38 39 static char attr[1024]; /* Extension of linebuf to hold attributes */ 40 static int curr; /* Index into linebuf */ 41 static int column; /* Printable length, accounting for 42 backspaces, etc. */ 43 static int lno_indent; /* Number of chars used for line number */ 44 static int overstrike; /* Next char should overstrike previous char */ 45 static int is_null_line; /* There is no current line */ 46 static char pendc; 47 static POSITION pendpos; 48 49 static int do_append(); 50 51 extern int bs_mode; 52 extern int tabstop; 53 extern int linenums; 54 extern int ctldisp; 55 extern int twiddle; 56 extern int binattr; 57 extern int auto_wrap, ignaw; 58 extern int bo_s_width, bo_e_width; 59 extern int ul_s_width, ul_e_width; 60 extern int bl_s_width, bl_e_width; 61 extern int so_s_width, so_e_width; 62 extern int sc_width, sc_height; 63 64 /* 65 * Rewind the line buffer. 66 */ 67 public void 68 prewind() 69 { 70 curr = 0; 71 column = 0; 72 overstrike = 0; 73 is_null_line = 0; 74 lno_indent = 0; 75 pendc = '\0'; 76 } 77 78 /* 79 * Insert the line number (of the given position) into the line buffer. 80 */ 81 public void 82 plinenum(pos) 83 POSITION pos; 84 { 85 register int lno; 86 register int i; 87 register int n; 88 89 /* 90 * We display the line number at the start of each line 91 * only if the -N option is set. 92 */ 93 if (linenums != OPT_ONPLUS) 94 return; 95 96 /* 97 * Get the line number and put it in the current line. 98 * {{ Note: since find_linenum calls forw_raw_line, 99 * it may seek in the input file, requiring the caller 100 * of plinenum to re-seek if necessary. }} 101 */ 102 lno = find_linenum(pos); 103 104 sprintf(&linebuf[curr], "%6d", lno); 105 n = strlen(&linebuf[curr]); 106 column += n; 107 for (i = 0; i < n; i++) 108 attr[curr++] = 0; 109 110 /* 111 * Append enough spaces to bring us to the next tab stop. 112 * {{ We could avoid this at the cost of adding some 113 * complication to the tab stop logic in pappend(). }} 114 */ 115 if (tabstop == 0) 116 tabstop = 1; 117 do 118 { 119 linebuf[curr] = ' '; 120 attr[curr++] = AT_NORMAL; 121 column++; 122 } while ((column % tabstop) != 0); 123 lno_indent = column; 124 } 125 126 /* 127 * Return the printing width of the start (enter) sequence 128 * for a given character attribute. 129 */ 130 int 131 attr_swidth(a) 132 int a; 133 { 134 switch (a) 135 { 136 case AT_BOLD: return (bo_s_width); 137 case AT_UNDERLINE: return (ul_s_width); 138 case AT_BLINK: return (bl_s_width); 139 case AT_STANDOUT: return (so_s_width); 140 } 141 return (0); 142 } 143 144 /* 145 * Return the printing width of the end (exit) sequence 146 * for a given character attribute. 147 */ 148 int 149 attr_ewidth(a) 150 int a; 151 { 152 switch (a) 153 { 154 case AT_BOLD: return (bo_e_width); 155 case AT_UNDERLINE: return (ul_e_width); 156 case AT_BLINK: return (bl_e_width); 157 case AT_STANDOUT: return (so_e_width); 158 } 159 return (0); 160 } 161 162 /* 163 * Return the printing width of a given character and attribute, 164 * if the character were added to the current position in the line buffer. 165 * Adding a character with a given attribute may cause an enter or exit 166 * attribute sequence to be inserted, so this must be taken into account. 167 */ 168 static int 169 pwidth(c, a) 170 int c; 171 int a; 172 { 173 register int w; 174 175 if (c == '\b') 176 /* 177 * Backspace moves backwards one position. 178 */ 179 return (-1); 180 181 if (control_char(c)) 182 /* 183 * Control characters do unpredicatable things, 184 * so we don't even try to guess; say it doesn't move. 185 * This can only happen if the -r flag is in effect. 186 */ 187 return (0); 188 189 /* 190 * Other characters take one space, 191 * plus the width of any attribute enter/exit sequence. 192 */ 193 w = 1; 194 if (curr > 0 && attr[curr-1] != a) 195 w += attr_ewidth(attr[curr-1]); 196 if (a && (curr == 0 || attr[curr-1] != a)) 197 w += attr_swidth(a); 198 return (w); 199 } 200 201 /* 202 * Delete the previous character in the line buffer. 203 */ 204 static void 205 backc() 206 { 207 curr--; 208 column -= pwidth(linebuf[curr], attr[curr]); 209 } 210 211 /* 212 * Append a character and attribute to the line buffer. 213 */ 214 static int 215 storec(c, a, pos) 216 int c; 217 int a; 218 POSITION pos; 219 { 220 register int w; 221 222 #if HILITE_SEARCH 223 if (is_hilited(pos, pos+1, 0)) 224 /* 225 * This character should be highlighted. 226 * Override the attribute passed in. 227 */ 228 a = AT_STANDOUT; 229 #endif 230 w = pwidth(c, a); 231 if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width) 232 /* 233 * Won't fit on screen. 234 */ 235 return (1); 236 237 if (curr >= sizeof(linebuf)-2) 238 /* 239 * Won't fit in line buffer. 240 */ 241 return (1); 242 243 /* 244 * Special handling for "magic cookie" terminals. 245 * If an attribute enter/exit sequence has a printing width > 0, 246 * and the sequence is adjacent to a space, delete the space. 247 * We just mark the space as invisible, to avoid having too 248 * many spaces deleted. 249 * {{ Note that even if the attribute width is > 1, we 250 * delete only one space. It's not worth trying to do more. 251 * It's hardly worth doing this much. }} 252 */ 253 if (curr > 0 && a != AT_NORMAL && 254 linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL && 255 attr_swidth(a) > 0) 256 { 257 /* 258 * We are about to append an enter-attribute sequence 259 * just after a space. Delete the space. 260 */ 261 attr[curr-1] = AT_INVIS; 262 column--; 263 } else if (curr > 0 && attr[curr-1] != AT_NORMAL && 264 attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL && 265 attr_ewidth(attr[curr-1]) > 0) 266 { 267 /* 268 * We are about to append a space just after an 269 * exit-attribute sequence. Delete the space. 270 */ 271 a = AT_INVIS; 272 column--; 273 } 274 /* End of magic cookie handling. */ 275 276 linebuf[curr] = c; 277 attr[curr] = a; 278 column += w; 279 return (0); 280 } 281 282 /* 283 * Append a character to the line buffer. 284 * Expand tabs into spaces, handle underlining, boldfacing, etc. 285 * Returns 0 if ok, 1 if couldn't fit in buffer. 286 */ 287 public int 288 pappend(c, pos) 289 register int c; 290 POSITION pos; 291 { 292 if (pendc) 293 { 294 if (do_append(pendc, pendpos)) 295 /* 296 * Oops. We've probably lost the char which 297 * was in pendc, since caller won't back up. 298 */ 299 return (1); 300 pendc = '\0'; 301 } 302 303 if (c == '\r' && bs_mode == BS_SPECIAL) 304 { 305 /* 306 * Don't put the CR into the buffer until we see 307 * the next char. If the next char is a newline, 308 * discard the CR. 309 */ 310 pendc = c; 311 pendpos = pos; 312 return (0); 313 } 314 315 return (do_append(c, pos)); 316 } 317 318 static int 319 do_append(c, pos) 320 int c; 321 POSITION pos; 322 { 323 register char *s; 324 register int a; 325 326 #define STOREC(c,a) \ 327 if (storec((c),(a),pos)) return (1); else curr++ 328 329 if (overstrike) 330 { 331 /* 332 * Overstrike the character at the current position 333 * in the line buffer. This will cause either 334 * underline (if a "_" is overstruck), 335 * bold (if an identical character is overstruck), 336 * or just deletion of the character in the buffer. 337 */ 338 overstrike = 0; 339 if ((char)c == linebuf[curr]) 340 STOREC(linebuf[curr], AT_BOLD); 341 else if (c == '_') 342 STOREC(linebuf[curr], AT_UNDERLINE); 343 else if (linebuf[curr] == '_') 344 STOREC(c, AT_UNDERLINE); 345 else if (control_char(c)) 346 goto do_control_char; 347 else 348 STOREC(c, AT_NORMAL); 349 } else if (c == '\b') 350 { 351 switch (bs_mode) 352 { 353 case BS_NORMAL: 354 STOREC(c, AT_NORMAL); 355 break; 356 case BS_CONTROL: 357 goto do_control_char; 358 case BS_SPECIAL: 359 if (curr == 0) 360 break; 361 backc(); 362 overstrike = 1; 363 break; 364 } 365 } else if (c == '\t') 366 { 367 /* 368 * Expand a tab into spaces. 369 */ 370 if (tabstop == 0) 371 tabstop = 1; 372 do 373 { 374 STOREC(' ', AT_NORMAL); 375 } while ((column % tabstop) != 0); 376 } else if (control_char(c)) 377 { 378 do_control_char: 379 if (ctldisp == 0) 380 { 381 /* 382 * Output as a normal character. 383 */ 384 STOREC(c, AT_NORMAL); 385 } else 386 { 387 /* 388 * Convert to printable representation. 389 */ 390 s = prchar(c); 391 a = binattr; 392 393 /* 394 * Make sure we can get the entire representation 395 * of the character on this line. 396 */ 397 if (column + (int) strlen(s) + 398 attr_swidth(a) + attr_ewidth(a) > sc_width) 399 return (1); 400 401 for ( ; *s != 0; s++) 402 STOREC(*s, a); 403 } 404 } else 405 { 406 STOREC(c, AT_NORMAL); 407 } 408 409 return (0); 410 } 411 412 /* 413 * Terminate the line in the line buffer. 414 */ 415 public void 416 pdone(endline) 417 int endline; 418 { 419 if (pendc && (pendc != '\r' || !endline)) 420 /* 421 * If we had a pending character, put it in the buffer. 422 * But discard a pending CR if we are at end of line 423 * (that is, discard the CR in a CR/LF sequence). 424 */ 425 (void) do_append(pendc, pendpos); 426 427 /* 428 * Add a newline if necessary, 429 * and append a '\0' to the end of the line. 430 */ 431 if (column < sc_width || !auto_wrap || ignaw || ctldisp == 0) 432 { 433 linebuf[curr] = '\n'; 434 attr[curr] = AT_NORMAL; 435 curr++; 436 } 437 linebuf[curr] = '\0'; 438 attr[curr] = AT_NORMAL; 439 } 440 441 /* 442 * Get a character from the current line. 443 * Return the character as the function return value, 444 * and the character attribute in *ap. 445 */ 446 public int 447 gline(i, ap) 448 register int i; 449 register int *ap; 450 { 451 char *s; 452 453 if (is_null_line) 454 { 455 /* 456 * If there is no current line, we pretend the line is 457 * either "~" or "", depending on the "twiddle" flag. 458 */ 459 *ap = AT_NORMAL; 460 s = (twiddle) ? "~\n" : "\n"; 461 return (s[i]); 462 } 463 464 *ap = attr[i]; 465 return (linebuf[i] & 0377); 466 } 467 468 /* 469 * Indicate that there is no current line. 470 */ 471 public void 472 null_line() 473 { 474 is_null_line = 1; 475 } 476 477 #if 1 478 /* 479 * Analogous to forw_line(), but deals with "raw lines": 480 * lines which are not split for screen width. 481 * {{ This is supposed to be more efficient than forw_line(). }} 482 */ 483 public POSITION 484 forw_raw_line(curr_pos, linep) 485 POSITION curr_pos; 486 char **linep; 487 { 488 register char *p; 489 register int c; 490 POSITION new_pos; 491 492 if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 493 (c = ch_forw_get()) == EOI) 494 return (NULL_POSITION); 495 496 p = linebuf; 497 498 for (;;) 499 { 500 if (c == '\n' || c == EOI) 501 { 502 new_pos = ch_tell(); 503 break; 504 } 505 if (p >= &linebuf[sizeof(linebuf)-1]) 506 { 507 /* 508 * Overflowed the input buffer. 509 * Pretend the line ended here. 510 * {{ The line buffer is supposed to be big 511 * enough that this never happens. }} 512 */ 513 new_pos = ch_tell() - 1; 514 break; 515 } 516 *p++ = c; 517 c = ch_forw_get(); 518 } 519 *p = '\0'; 520 if (linep != NULL) 521 *linep = linebuf; 522 return (new_pos); 523 } 524 525 /* 526 * Analogous to back_line(), but deals with "raw lines". 527 * {{ This is supposed to be more efficient than back_line(). }} 528 */ 529 public POSITION 530 back_raw_line(curr_pos, linep) 531 POSITION curr_pos; 532 char **linep; 533 { 534 register char *p; 535 register int c; 536 POSITION new_pos; 537 538 if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || 539 ch_seek(curr_pos-1)) 540 return (NULL_POSITION); 541 542 p = &linebuf[sizeof(linebuf)]; 543 *--p = '\0'; 544 545 for (;;) 546 { 547 c = ch_back_get(); 548 if (c == '\n') 549 { 550 /* 551 * This is the newline ending the previous line. 552 * We have hit the beginning of the line. 553 */ 554 new_pos = ch_tell() + 1; 555 break; 556 } 557 if (c == EOI) 558 { 559 /* 560 * We have hit the beginning of the file. 561 * This must be the first line in the file. 562 * This must, of course, be the beginning of the line. 563 */ 564 new_pos = ch_zero(); 565 break; 566 } 567 if (p <= linebuf) 568 { 569 /* 570 * Overflowed the input buffer. 571 * Pretend the line ended here. 572 */ 573 new_pos = ch_tell() + 1; 574 break; 575 } 576 *--p = c; 577 } 578 if (linep != NULL) 579 *linep = p; 580 return (new_pos); 581 } 582 #endif 583