|
|
1.1 ! root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.