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_UNDERLINE mode.  The complexity arises from the attempt
  17:  * to deal with all cases, especially involving long lines with underlining.
  18:  * There are still some cases which will break it.
  19:  *
  20:  * There are four states:
  21:  *	UL_NORMAL is the normal state (not in underline mode).
  22:  *	UL_YES 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 just some ordinary characters
  25:  *		(no backspaces) to end underline mode.
  26:  *	UL_X means we are one character after UL_YES
  27:  *		(we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
  28:  *	UL_XB means we are one character after UL_X
  29:  *		(we have gotten the backspace in "_\bX" or "X\b_";
  30:  *		we expect one more ordinary character,
  31:  *		which will put us back in state UL_YES).
  32:  */
  33: static int ul_state;        /* Currently in underline mode? */
  34: #define UL_NORMAL   0   /* Not in underline mode */
  35: #define UL_YES      1   /* In underline, need next char */
  36: #define UL_X        2   /* In underline, got char, need \b */
  37: #define UL_XB       3   /* In underline, got char & \b, need one more */
  38: 
  39: public char *line;      /* Pointer to the current line.
  40: 				   Usually points to linebuf. */
  41: 
  42: extern int bs_mode;
  43: extern int tabstop;
  44: extern int ul_width, ue_width;
  45: extern int sc_width, sc_height;
  46: 
  47: /*
  48:  * Rewind the line buffer.
  49:  */
  50:     public void
  51: prewind()
  52: {
  53:     line = curr = linebuf;
  54:     ul_state = UL_NORMAL;
  55:     column = 0;
  56: }
  57: 
  58: /*
  59:  * Append a character to the line buffer.
  60:  * Expand tabs into spaces, handle underlining.
  61:  * Returns 0 if ok, 1 if couldn't fit in buffer.
  62:  */
  63: 
  64: #define NEW_COLUMN(newcol)  if ((newcol) + ((ul_state)?ue_width:0) > sc_width) \
  65:                     return (1); else column = (newcol)
  66: 
  67:     public int
  68: pappend(c)
  69:     int c;
  70: {
  71:     if (c == '\0')
  72:     {
  73:         /*
  74: 		 * Terminate underline mode, if necessary.
  75: 		 * Append a '\0' to the end of the line.
  76: 		 */
  77:         switch (ul_state)
  78:         {
  79:         case UL_X:
  80:             curr[0] = curr[-1];
  81:             curr[-1] = UE_CHAR;
  82:             curr++;
  83:             break;
  84:         case UL_XB:
  85:         case UL_YES:
  86:             *curr++ = UE_CHAR;
  87:             break;
  88:         }
  89:         ul_state = UL_NORMAL;
  90:         *curr = '\0';
  91:         return (0);
  92:     }
  93: 
  94:     if (curr > linebuf + sizeof(linebuf) - 12)
  95:         /*
  96: 		 * Almost out of room in the line buffer.
  97: 		 * Don't take any chances.
  98: 		 * {{ Linebuf is supposed to be big enough that this
  99: 		 *    will never happen, but may need to be made
 100: 		 *    bigger for wide screens or lots of backspaces. }}
 101: 		 */
 102:         return (1);
 103: 
 104:     if (bs_mode == BS_UNDERLINE)
 105:     {
 106:         /*
 107: 		 * Advance the state machine.
 108: 		 */
 109:         switch (ul_state)
 110:         {
 111:         case UL_NORMAL:
 112:             if (curr <= linebuf + 1 || curr[-1] != '\b')
 113:                 break;
 114:             if (c != '_' && curr[-2] != '_')
 115:             {
 116:                 curr -= 2;
 117:                 break;
 118:             }
 119: 
 120:             /*
 121: 			 * We have either "_\bX" or "X\b_" (including
 122: 			 * the current char).  Switch into underline mode.
 123: 			 */
 124:             if (column + ul_width + ue_width + 1 >= sc_width)
 125:                 /*
 126: 				 * Not enough room left on the screen to
 127: 				 * enter and exit underline mode.
 128: 				 */
 129:                 return (1);
 130: 
 131:             if (ul_width > 0 &&
 132:                 curr > linebuf + 2 && curr[-3] == ' ')
 133:             {
 134:                 /*
 135: 				 * Special case for magic cookie terminals:
 136: 				 * if the previous char was a space, replace
 137: 				 * it with the "enter underline" sequence.
 138: 				 */
 139:                 curr[-3] = UL_CHAR;
 140:                 column += ul_width-1;
 141:             } else
 142:             {
 143:                 curr[-1] = curr[-2];
 144:                 curr[-2] = UL_CHAR;
 145:                 column += ul_width;
 146:                 curr++;
 147:             }
 148:             /* Fall thru */
 149:         case UL_XB:
 150:             /*
 151: 			 * Termination of a sequnce "_\bX" or "X\b_".
 152: 			 */
 153:             if (c == '_')
 154:                 c = curr[-2];
 155:             curr -= 2;
 156:             ul_state = UL_YES;
 157:             break;
 158:         case UL_YES:
 159:             if (column + ue_width + 1 >= sc_width)
 160:                 /*
 161: 				 * We have just barely enough room to
 162: 				 * exit underline mode.
 163: 				 */
 164:                 return (1);
 165:             ul_state = UL_X;
 166:             break;
 167:         case UL_X:
 168:             if (c == '\b')
 169:                 ul_state = UL_XB;
 170:             else
 171:             {
 172:                 /*
 173: 				 * Exit underline mode.
 174: 				 * We have to shuffle the chars a bit
 175: 				 * to make this work.
 176: 				 */
 177:                 curr[0] = curr[-1];
 178:                 curr[-1] = UE_CHAR;
 179:                 column += ue_width;
 180:                 if (ul_width > 0 && curr[0] == ' ')
 181:                     /*
 182: 					 * Another special case for magic
 183: 					 * cookie terminals: if the next
 184: 					 * char is a space, replace it
 185: 					 * with the "exit underline" sequence.
 186: 					 */
 187:                     column--;
 188:                 else
 189:                     curr++;
 190:                 ul_state = UL_NORMAL;
 191:             }
 192:             break;
 193:         }
 194:     }
 195: 
 196:     if (c == '\t')
 197:     {
 198:         /*
 199: 		 * Expand a tab into spaces.
 200: 		 */
 201:         do
 202:         {
 203:             NEW_COLUMN(column+1);
 204:         } while ((column % tabstop) != 0);
 205:         *curr++ = '\t';
 206:         return (0);
 207:     }
 208: 
 209:     if (c == '\b')
 210:     {
 211:         if (bs_mode == BS_CONTROL)
 212:         {
 213:             /*
 214: 			 * Treat backspace as a control char: output "^H".
 215: 			 */
 216:             NEW_COLUMN(column+2);
 217:             *curr++ = ('H' | 0200);
 218:         } else
 219:         {
 220:             /*
 221: 			 * Output a real backspace.
 222: 			 */
 223:             column--;
 224:             *curr++ = '\b';
 225:         }
 226:         return (0);
 227:     }
 228: 
 229:     if (control_char(c))
 230:     {
 231:         /*
 232: 		 * Put a "^X" into the buffer.
 233: 		 * The 0200 bit is used to tell put_line() to prefix
 234: 		 * the char with a ^.  We don't actually put the ^
 235: 		 * in the buffer because we sometimes need to move
 236: 		 * chars around, and such movement might separate
 237: 		 * the ^ from its following character.
 238: 		 */
 239:         NEW_COLUMN(column+2);
 240:         *curr++ = (carat_char(c) | 0200);
 241:         return (0);
 242:     }
 243: 
 244:     /*
 245: 	 * Ordinary character.  Just put it in the buffer.
 246: 	 */
 247:     NEW_COLUMN(column+1);
 248:     *curr++ = c;
 249:     return (0);
 250: }
 251: 
 252: /*
 253:  * Analogous to forw_line(), but deals with "raw lines":
 254:  * lines which are not split for screen width.
 255:  * {{ This is supposed to be more efficient than forw_line(). }}
 256:  */
 257:     public POSITION
 258: forw_raw_line(curr_pos)
 259:     POSITION curr_pos;
 260: {
 261:     register char *p;
 262:     register int c;
 263:     POSITION new_pos;
 264: 
 265:     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
 266:         (c = ch_forw_get()) == EOF)
 267:         return (NULL_POSITION);
 268: 
 269:     p = linebuf;
 270: 
 271:     for (;;)
 272:     {
 273:         if (c == '\n' || c == EOF)
 274:         {
 275:             new_pos = ch_tell();
 276:             break;
 277:         }
 278:         if (p >= &linebuf[sizeof(linebuf)-1])
 279:         {
 280:             /*
 281: 			 * Overflowed the input buffer.
 282: 			 * Pretend the line ended here.
 283: 			 * {{ The line buffer is supposed to be big
 284: 			 *    enough that this never happens. }}
 285: 			 */
 286:             new_pos = ch_tell() - 1;
 287:             break;
 288:         }
 289:         *p++ = c;
 290:         c = ch_forw_get();
 291:     }
 292:     *p = '\0';
 293:     line = linebuf;
 294:     return (new_pos);
 295: }
 296: 
 297: /*
 298:  * Analogous to back_line(), but deals with "raw lines".
 299:  * {{ This is supposed to be more efficient than back_line(). }}
 300:  */
 301:     public POSITION
 302: back_raw_line(curr_pos)
 303:     POSITION curr_pos;
 304: {
 305:     register char *p;
 306:     register int c;
 307:     POSITION new_pos;
 308: 
 309:     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
 310:         ch_seek(curr_pos-1))
 311:         return (NULL_POSITION);
 312: 
 313:     p = &linebuf[sizeof(linebuf)];
 314:     *--p = '\0';
 315: 
 316:     for (;;)
 317:     {
 318:         c = ch_back_get();
 319:         if (c == '\n')
 320:         {
 321:             /*
 322: 			 * This is the newline ending the previous line.
 323: 			 * We have hit the beginning of the line.
 324: 			 */
 325:             new_pos = ch_tell() + 1;
 326:             break;
 327:         }
 328:         if (c == EOF)
 329:         {
 330:             /*
 331: 			 * We have hit the beginning of the file.
 332: 			 * This must be the first line in the file.
 333: 			 * This must, of course, be the beginning of the line.
 334: 			 */
 335:             new_pos = (POSITION)0;
 336:             break;
 337:         }
 338:         if (p <= linebuf)
 339:         {
 340:             /*
 341: 			 * Overflowed the input buffer.
 342: 			 * Pretend the line ended here.
 343: 			 */
 344:             new_pos = ch_tell() + 1;
 345:             break;
 346:         }
 347:         *--p = c;
 348:     }
 349:     line = p;
 350:     return (new_pos);
 351: }

Defined functions

Defined variables

column defined in line 12; used 14 times
curr defined in line 11; used 31 times
linebuf defined in line 10; used 12 times
public defined in line 67; never used
ul_state defined in line 33; used 9 times

Defined macros

NEW_COLUMN defined in line 64; used 4 times
UL_NORMAL defined in line 34; used 3 times
UL_X defined in line 36; used 1 times
UL_XB defined in line 37; used 1 times
UL_YES defined in line 35; used 1 times
Last modified: 1986-04-21
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1598
Valid CSS Valid XHTML 1.0 Strict