1: /*
   2:  * Routines to manipulate the "line buffer".
   3:  * The line buffer holds a line of output as it is being built
   4:  * in preparation for output to the screen.
   5:  * We keep track of the PRINTABLE length of the line as it is being built.
   6:  */
   7: 
   8: #include "less.h"
   9: 
  10: static char linebuf[1024];  /* Buffer which holds the current output line */
  11: static char *curr;      /* Pointer into linebuf */
  12: static int column;      /* Printable length, accounting for
  13: 				   backspaces, etc. */
  14: /*
  15:  * A ridiculously complex state machine takes care of backspaces
  16:  * when in BS_SPECIAL mode.  The complexity arises from the attempt
  17:  * to deal with all cases, especially involving long lines with underlining,
  18:  * boldfacing or whatever.  There are still some cases which will break it.
  19:  *
  20:  * There are four states:
  21:  *	LN_NORMAL is the normal state (not in underline mode).
  22:  *	LN_UNDERLINE means we are in underline mode.  We expect to get
  23:  *		either a sequence like "_\bX" or "X\b_" to continue
  24:  *		underline mode, or anything else to end underline mode.
  25:  *	LN_BOLDFACE means we are in boldface mode.  We expect to get sequences
  26:  *		like "X\bX\b...X\bX" to continue boldface mode, or anything
  27:  *		else to end boldface mode.
  28:  *	LN_UL_X means we are one character after LN_UNDERLINE
  29:  *		(we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
  30:  *	LN_UL_XB means we are one character after LN_UL_X
  31:  *		(we have gotten the backspace in "_\bX" or "X\b_";
  32:  *		we expect one more ordinary character,
  33:  *		which will put us back in state LN_UNDERLINE).
  34:  *	LN_BO_X means we are one character after LN_BOLDFACE
  35:  *		(we have gotten the 'X' in "X\bX").
  36:  *	LN_BO_XB means we are one character after LN_BO_X
  37:  *		(we have gotten the backspace in "X\bX";
  38:  *		we expect one more 'X' which will put us back
  39:  *		in LN_BOLDFACE).
  40:  */
  41: static int ln_state;        /* Currently in normal/underline/bold/etc mode? */
  42: #define LN_NORMAL   0   /* Not in underline, boldface or whatever mode */
  43: #define LN_UNDERLINE    1   /* In underline, need next char */
  44: #define LN_UL_X     2   /* In underline, got char, need \b */
  45: #define LN_UL_XB    3   /* In underline, got char & \b, need one more */
  46: #define LN_BOLDFACE 4   /* In boldface, need next char */
  47: #define LN_BO_X     5   /* In boldface, got char, need \b */
  48: #define LN_BO_XB    6   /* In boldface, got char & \b, need same char */
  49: 
  50: public char *line;      /* Pointer to the current line.
  51: 				   Usually points to linebuf. */
  52: 
  53: extern int bs_mode;
  54: extern int tabstop;
  55: extern int bo_width, be_width;
  56: extern int ul_width, ue_width;
  57: extern int sc_width, sc_height;
  58: 
  59: /*
  60:  * Rewind the line buffer.
  61:  */
  62:     public void
  63: prewind()
  64: {
  65:     line = curr = linebuf;
  66:     ln_state = LN_NORMAL;
  67:     column = 0;
  68: }
  69: 
  70: /*
  71:  * Append a character to the line buffer.
  72:  * Expand tabs into spaces, handle underlining, boldfacing, etc.
  73:  * Returns 0 if ok, 1 if couldn't fit in buffer.
  74:  */
  75: 
  76: #define NEW_COLUMN(newcol)  if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
  77:                     return (1); else column = (newcol)
  78: 
  79:     public int
  80: pappend(c)
  81:     int c;
  82: {
  83:     if (c == '\0')
  84:     {
  85:         /*
  86: 		 * Terminate any special modes, if necessary.
  87: 		 * Append a '\0' to the end of the line.
  88: 		 */
  89:         switch (ln_state)
  90:         {
  91:         case LN_UL_X:
  92:             curr[0] = curr[-1];
  93:             curr[-1] = UE_CHAR;
  94:             curr++;
  95:             break;
  96:         case LN_BO_X:
  97:             curr[0] = curr[-1];
  98:             curr[-1] = BE_CHAR;
  99:             curr++;
 100:             break;
 101:         case LN_UL_XB:
 102:         case LN_UNDERLINE:
 103:             *curr++ = UE_CHAR;
 104:             break;
 105:         case LN_BO_XB:
 106:         case LN_BOLDFACE:
 107:             *curr++ = BE_CHAR;
 108:             break;
 109:         }
 110:         ln_state = LN_NORMAL;
 111:         *curr = '\0';
 112:         return (0);
 113:     }
 114: 
 115:     if (curr > linebuf + sizeof(linebuf) - 12)
 116:         /*
 117: 		 * Almost out of room in the line buffer.
 118: 		 * Don't take any chances.
 119: 		 * {{ Linebuf is supposed to be big enough that this
 120: 		 *    will never happen, but may need to be made
 121: 		 *    bigger for wide screens or lots of backspaces. }}
 122: 		 */
 123:         return (1);
 124: 
 125:     if (bs_mode == BS_SPECIAL)
 126:     {
 127:         /*
 128: 		 * Advance the state machine.
 129: 		 */
 130:         switch (ln_state)
 131:         {
 132:         case LN_NORMAL:
 133:             if (curr <= linebuf + 1 || curr[-1] != '\b')
 134:                 break;
 135: 
 136:             if (c == curr[-2])
 137:                 goto enter_boldface;
 138:             if (c == '_' || curr[-2] == '_')
 139:                 goto enter_underline;
 140:             curr -= 2;
 141:             break;
 142: 
 143: enter_boldface:
 144:             /*
 145: 			 * We have "X\bX" (including the current char).
 146: 			 * Switch into boldface mode.
 147: 			 */
 148:             if (column + bo_width + be_width + 1 >= sc_width)
 149:                 /*
 150: 				 * Not enough room left on the screen to
 151: 				 * enter and exit boldface mode.
 152: 				 */
 153:                 return (1);
 154: 
 155:             if (bo_width > 0 &&
 156:                 curr > linebuf + 2 && curr[-3] == ' ')
 157:             {
 158:                 /*
 159: 				 * Special case for magic cookie terminals:
 160: 				 * if the previous char was a space, replace
 161: 				 * it with the "enter boldface" sequence.
 162: 				 */
 163:                 curr[-3] = BO_CHAR;
 164:                 column += bo_width-1;
 165:             } else
 166:             {
 167:                 curr[-1] = curr[-2];
 168:                 curr[-2] = BO_CHAR;
 169:                 column += bo_width;
 170:                 curr++;
 171:             }
 172:             goto ln_bo_xb_case;
 173: 
 174: enter_underline:
 175:             /*
 176: 			 * We have either "_\bX" or "X\b_" (including
 177: 			 * the current char).  Switch into underline mode.
 178: 			 */
 179:             if (column + ul_width + ue_width + 1 >= sc_width)
 180:                 /*
 181: 				 * Not enough room left on the screen to
 182: 				 * enter and exit underline mode.
 183: 				 */
 184:                 return (1);
 185: 
 186:             if (ul_width > 0 &&
 187:                 curr > linebuf + 2 && curr[-3] == ' ')
 188:             {
 189:                 /*
 190: 				 * Special case for magic cookie terminals:
 191: 				 * if the previous char was a space, replace
 192: 				 * it with the "enter underline" sequence.
 193: 				 */
 194:                 curr[-3] = UL_CHAR;
 195:                 column += ul_width-1;
 196:             } else
 197:             {
 198:                 curr[-1] = curr[-2];
 199:                 curr[-2] = UL_CHAR;
 200:                 column += ul_width;
 201:                 curr++;
 202:             }
 203:             goto ln_ul_xb_case;
 204:             /*NOTREACHED*/
 205:         case LN_UL_XB:
 206:             /*
 207: 			 * Termination of a sequence "_\bX" or "X\b_".
 208: 			 */
 209:             if (c != '_' && curr[-2] != '_' && c == curr[-2])
 210:             {
 211:                 /*
 212: 				 * We seem to have run on from underlining
 213: 				 * into boldfacing - this is a nasty fix, but
 214: 				 * until this whole routine is rewritten as a
 215: 				 * real DFA, ...  well ...
 216: 				 */
 217:                 curr[0] = curr[-2];
 218:                 curr[-2] = UE_CHAR;
 219:                 curr[-1] = BO_CHAR;
 220:                 curr += 2; /* char & non-existent backspace */
 221:                 ln_state = LN_BO_XB;
 222:                 goto ln_bo_xb_case;
 223:             }
 224: ln_ul_xb_case:
 225:             if (c == '_')
 226:                 c = curr[-2];
 227:             curr -= 2;
 228:             ln_state = LN_UNDERLINE;
 229:             break;
 230:         case LN_BO_XB:
 231:             /*
 232: 			 * Termination of a sequnce "X\bX".
 233: 			 */
 234:             if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
 235:             {
 236:                 /*
 237: 				 * We seem to have run on from
 238: 				 * boldfacing into underlining.
 239: 				 */
 240:                 curr[0] = curr[-2];
 241:                 curr[-2] = BE_CHAR;
 242:                 curr[-1] = UL_CHAR;
 243:                 curr += 2; /* char & non-existent backspace */
 244:                 ln_state = LN_UL_XB;
 245:                 goto ln_ul_xb_case;
 246:             }
 247: ln_bo_xb_case:
 248:             curr -= 2;
 249:             ln_state = LN_BOLDFACE;
 250:             break;
 251:         case LN_UNDERLINE:
 252:             if (column + ue_width + bo_width + 1 + be_width >= sc_width)
 253:                 /*
 254: 				 * We have just barely enough room to
 255: 				 * exit underline mode and handle a possible
 256: 				 * underline/boldface run on mixup.
 257: 				 */
 258:                 return (1);
 259:             ln_state = LN_UL_X;
 260:             break;
 261:         case LN_BOLDFACE:
 262:             if (c == '\b')
 263:             {
 264:                 ln_state = LN_BO_XB;
 265:                 break;
 266:             }
 267:             if (column + be_width + ul_width + 1 + ue_width >= sc_width)
 268:                 /*
 269: 				 * We have just barely enough room to
 270: 				 * exit underline mode and handle a possible
 271: 				 * underline/boldface run on mixup.
 272: 				 */
 273:                 return (1);
 274:             ln_state = LN_BO_X;
 275:             break;
 276:         case LN_UL_X:
 277:             if (c == '\b')
 278:                 ln_state = LN_UL_XB;
 279:             else
 280:             {
 281:                 /*
 282: 				 * Exit underline mode.
 283: 				 * We have to shuffle the chars a bit
 284: 				 * to make this work.
 285: 				 */
 286:                 curr[0] = curr[-1];
 287:                 curr[-1] = UE_CHAR;
 288:                 column += ue_width;
 289:                 if (ue_width > 0 && curr[0] == ' ')
 290:                     /*
 291: 					 * Another special case for magic
 292: 					 * cookie terminals: if the next
 293: 					 * char is a space, replace it
 294: 					 * with the "exit underline" sequence.
 295: 					 */
 296:                     column--;
 297:                 else
 298:                     curr++;
 299:                 ln_state = LN_NORMAL;
 300:             }
 301:             break;
 302:         case LN_BO_X:
 303:             if (c == '\b')
 304:                 ln_state = LN_BO_XB;
 305:             else
 306:             {
 307:                 /*
 308: 				 * Exit boldface mode.
 309: 				 * We have to shuffle the chars a bit
 310: 				 * to make this work.
 311: 				 */
 312:                 curr[0] = curr[-1];
 313:                 curr[-1] = BE_CHAR;
 314:                 column += be_width;
 315:                 if (be_width > 0 && curr[0] == ' ')
 316:                     /*
 317: 					 * Another special case for magic
 318: 					 * cookie terminals: if the next
 319: 					 * char is a space, replace it
 320: 					 * with the "exit boldface" sequence.
 321: 					 */
 322:                     column--;
 323:                 else
 324:                     curr++;
 325:                 ln_state = LN_NORMAL;
 326:             }
 327:             break;
 328:         }
 329:     }
 330: 
 331:     if (c == '\t')
 332:     {
 333:         /*
 334: 		 * Expand a tab into spaces.
 335: 		 */
 336:         do
 337:         {
 338:             NEW_COLUMN(column+1);
 339:         } while ((column % tabstop) != 0);
 340:         *curr++ = '\t';
 341:         return (0);
 342:     }
 343: 
 344:     if (c == '\b')
 345:     {
 346:         if (bs_mode == BS_CONTROL)
 347:         {
 348:             /*
 349: 			 * Treat backspace as a control char: output "^H".
 350: 			 */
 351:             NEW_COLUMN(column+2);
 352:             *curr++ = ('H' | 0200);
 353:         } else
 354:         {
 355:             /*
 356: 			 * Output a real backspace.
 357: 			 */
 358:             column--;
 359:             *curr++ = '\b';
 360:         }
 361:         return (0);
 362:     }
 363: 
 364:     if (control_char(c))
 365:     {
 366:         /*
 367: 		 * Put a "^X" into the buffer.
 368: 		 * The 0200 bit is used to tell put_line() to prefix
 369: 		 * the char with a ^.  We don't actually put the ^
 370: 		 * in the buffer because we sometimes need to move
 371: 		 * chars around, and such movement might separate
 372: 		 * the ^ from its following character.
 373: 		 * {{ This should be redone so that we can use an
 374: 		 *    8 bit (e.g. international) character set. }}
 375: 		 */
 376:         NEW_COLUMN(column+2);
 377:         *curr++ = (carat_char(c) | 0200);
 378:         return (0);
 379:     }
 380: 
 381:     /*
 382: 	 * Ordinary character.  Just put it in the buffer.
 383: 	 */
 384:     NEW_COLUMN(column+1);
 385:     *curr++ = c;
 386:     return (0);
 387: }
 388: 
 389: /*
 390:  * Analogous to forw_line(), but deals with "raw lines":
 391:  * lines which are not split for screen width.
 392:  * {{ This is supposed to be more efficient than forw_line(). }}
 393:  */
 394:     public POSITION
 395: forw_raw_line(curr_pos)
 396:     POSITION curr_pos;
 397: {
 398:     register char *p;
 399:     register int c;
 400:     POSITION new_pos;
 401: 
 402:     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
 403:         (c = ch_forw_get()) == EOF)
 404:         return (NULL_POSITION);
 405: 
 406:     p = linebuf;
 407: 
 408:     for (;;)
 409:     {
 410:         if (c == '\n' || c == EOF)
 411:         {
 412:             new_pos = ch_tell();
 413:             break;
 414:         }
 415:         if (p >= &linebuf[sizeof(linebuf)-1])
 416:         {
 417:             /*
 418: 			 * Overflowed the input buffer.
 419: 			 * Pretend the line ended here.
 420: 			 * {{ The line buffer is supposed to be big
 421: 			 *    enough that this never happens. }}
 422: 			 */
 423:             new_pos = ch_tell() - 1;
 424:             break;
 425:         }
 426:         *p++ = c;
 427:         c = ch_forw_get();
 428:     }
 429:     *p = '\0';
 430:     line = linebuf;
 431:     return (new_pos);
 432: }
 433: 
 434: /*
 435:  * Analogous to back_line(), but deals with "raw lines".
 436:  * {{ This is supposed to be more efficient than back_line(). }}
 437:  */
 438:     public POSITION
 439: back_raw_line(curr_pos)
 440:     POSITION curr_pos;
 441: {
 442:     register char *p;
 443:     register int c;
 444:     POSITION new_pos;
 445: 
 446:     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
 447:         ch_seek(curr_pos-1))
 448:         return (NULL_POSITION);
 449: 
 450:     p = &linebuf[sizeof(linebuf)];
 451:     *--p = '\0';
 452: 
 453:     for (;;)
 454:     {
 455:         c = ch_back_get();
 456:         if (c == '\n')
 457:         {
 458:             /*
 459: 			 * This is the newline ending the previous line.
 460: 			 * We have hit the beginning of the line.
 461: 			 */
 462:             new_pos = ch_tell() + 1;
 463:             break;
 464:         }
 465:         if (c == EOF)
 466:         {
 467:             /*
 468: 			 * We have hit the beginning of the file.
 469: 			 * This must be the first line in the file.
 470: 			 * This must, of course, be the beginning of the line.
 471: 			 */
 472:             new_pos = (POSITION)0;
 473:             break;
 474:         }
 475:         if (p <= linebuf)
 476:         {
 477:             /*
 478: 			 * Overflowed the input buffer.
 479: 			 * Pretend the line ended here.
 480: 			 */
 481:             new_pos = ch_tell() + 1;
 482:             break;
 483:         }
 484:         *--p = c;
 485:     }
 486:     line = p;
 487:     return (new_pos);
 488: }

Defined functions

back_raw_line defined in line 438; used 2 times
forw_raw_line defined in line 394; used 2 times
pappend defined in line 79; used 6 times
prewind defined in line 62; used 3 times

Defined variables

column defined in line 12; used 20 times
curr defined in line 11; used 64 times
line defined in line 50; used 13 times
linebuf defined in line 10; used 13 times
ln_state defined in line 41; used 16 times
public defined in line 79; never used

Defined macros

LN_BOLDFACE defined in line 46; used 1 times
LN_BO_X defined in line 47; used 1 times
LN_BO_XB defined in line 48; used 3 times
LN_NORMAL defined in line 42; used 4 times
LN_UL_X defined in line 44; used 1 times
LN_UL_XB defined in line 45; used 2 times
LN_UNDERLINE defined in line 43; used 1 times
NEW_COLUMN defined in line 76; used 4 times
Last modified: 1990-07-10
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2752
Valid CSS Valid XHTML 1.0 Strict