Annotation of src/usr.bin/more/line.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2: * Copyright (c) 1988 Mark Nudleman
! 3: * Copyright (c) 1988 Regents of the University of California.
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: * 1. Redistributions of source code must retain the above copyright
! 10: * notice, this list of conditions and the following disclaimer.
! 11: * 2. Redistributions in binary form must reproduce the above copyright
! 12: * notice, this list of conditions and the following disclaimer in the
! 13: * documentation and/or other materials provided with the distribution.
! 14: * 3. All advertising materials mentioning features or use of this software
! 15: * must display the following acknowledgement:
! 16: * This product includes software developed by the University of
! 17: * California, Berkeley and its contributors.
! 18: * 4. Neither the name of the University nor the names of its contributors
! 19: * may be used to endorse or promote products derived from this software
! 20: * without specific prior written permission.
! 21: *
! 22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 32: * SUCH DAMAGE.
! 33: */
! 34:
! 35: #ifndef lint
! 36: /* from: static char sccsid[] = "@(#)line.c 5.5 (Berkeley) 7/24/91"; */
! 37: static char *rcsid = "$Id: line.c,v 1.2 1993/11/09 05:09:49 cgd Exp $";
! 38: #endif /* not lint */
! 39:
! 40: /*
! 41: * Routines to manipulate the "line buffer".
! 42: * The line buffer holds a line of output as it is being built
! 43: * in preparation for output to the screen.
! 44: * We keep track of the PRINTABLE length of the line as it is being built.
! 45: */
! 46:
! 47: #include <sys/types.h>
! 48: #include <ctype.h>
! 49: #include <less.h>
! 50:
! 51: static char linebuf[1024]; /* Buffer which holds the current output line */
! 52: static char *curr; /* Pointer into linebuf */
! 53: static int column; /* Printable length, accounting for
! 54: backspaces, etc. */
! 55: /*
! 56: * A ridiculously complex state machine takes care of backspaces. The
! 57: * complexity arises from the attempt to deal with all cases, especially
! 58: * involving long lines with underlining, boldfacing or whatever. There
! 59: * are still some cases which will break it.
! 60: *
! 61: * There are four states:
! 62: * LN_NORMAL is the normal state (not in underline mode).
! 63: * LN_UNDERLINE means we are in underline mode. We expect to get
! 64: * either a sequence like "_\bX" or "X\b_" to continue
! 65: * underline mode, or anything else to end underline mode.
! 66: * LN_BOLDFACE means we are in boldface mode. We expect to get sequences
! 67: * like "X\bX\b...X\bX" to continue boldface mode, or anything
! 68: * else to end boldface mode.
! 69: * LN_UL_X means we are one character after LN_UNDERLINE
! 70: * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
! 71: * LN_UL_XB means we are one character after LN_UL_X
! 72: * (we have gotten the backspace in "_\bX" or "X\b_";
! 73: * we expect one more ordinary character,
! 74: * which will put us back in state LN_UNDERLINE).
! 75: * LN_BO_X means we are one character after LN_BOLDFACE
! 76: * (we have gotten the 'X' in "X\bX").
! 77: * LN_BO_XB means we are one character after LN_BO_X
! 78: * (we have gotten the backspace in "X\bX";
! 79: * we expect one more 'X' which will put us back
! 80: * in LN_BOLDFACE).
! 81: */
! 82: static int ln_state; /* Currently in normal/underline/bold/etc mode? */
! 83: #define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */
! 84: #define LN_UNDERLINE 1 /* In underline, need next char */
! 85: #define LN_UL_X 2 /* In underline, got char, need \b */
! 86: #define LN_UL_XB 3 /* In underline, got char & \b, need one more */
! 87: #define LN_BOLDFACE 4 /* In boldface, need next char */
! 88: #define LN_BO_X 5 /* In boldface, got char, need \b */
! 89: #define LN_BO_XB 6 /* In boldface, got char & \b, need same char */
! 90:
! 91: char *line; /* Pointer to the current line.
! 92: Usually points to linebuf. */
! 93:
! 94: extern int bs_mode;
! 95: extern int tabstop;
! 96: extern int bo_width, be_width;
! 97: extern int ul_width, ue_width;
! 98: extern int sc_width, sc_height;
! 99:
! 100: /*
! 101: * Rewind the line buffer.
! 102: */
! 103: prewind()
! 104: {
! 105: line = curr = linebuf;
! 106: ln_state = LN_NORMAL;
! 107: column = 0;
! 108: }
! 109:
! 110: /*
! 111: * Append a character to the line buffer.
! 112: * Expand tabs into spaces, handle underlining, boldfacing, etc.
! 113: * Returns 0 if ok, 1 if couldn't fit in buffer.
! 114: */
! 115: #define NEW_COLUMN(addon) \
! 116: if (column + addon + (ln_state ? ue_width : 0) > sc_width) \
! 117: return(1); \
! 118: else \
! 119: column += addon
! 120:
! 121: pappend(c)
! 122: int c;
! 123: {
! 124: if (c == '\0') {
! 125: /*
! 126: * Terminate any special modes, if necessary.
! 127: * Append a '\0' to the end of the line.
! 128: */
! 129: switch (ln_state) {
! 130: case LN_UL_X:
! 131: curr[0] = curr[-1];
! 132: curr[-1] = UE_CHAR;
! 133: curr++;
! 134: break;
! 135: case LN_BO_X:
! 136: curr[0] = curr[-1];
! 137: curr[-1] = BE_CHAR;
! 138: curr++;
! 139: break;
! 140: case LN_UL_XB:
! 141: case LN_UNDERLINE:
! 142: *curr++ = UE_CHAR;
! 143: break;
! 144: case LN_BO_XB:
! 145: case LN_BOLDFACE:
! 146: *curr++ = BE_CHAR;
! 147: break;
! 148: }
! 149: ln_state = LN_NORMAL;
! 150: *curr = '\0';
! 151: return(0);
! 152: }
! 153:
! 154: if (curr > linebuf + sizeof(linebuf) - 12)
! 155: /*
! 156: * Almost out of room in the line buffer.
! 157: * Don't take any chances.
! 158: * {{ Linebuf is supposed to be big enough that this
! 159: * will never happen, but may need to be made
! 160: * bigger for wide screens or lots of backspaces. }}
! 161: */
! 162: return(1);
! 163:
! 164: if (!bs_mode) {
! 165: /*
! 166: * Advance the state machine.
! 167: */
! 168: switch (ln_state) {
! 169: case LN_NORMAL:
! 170: if (curr <= linebuf + 1
! 171: || curr[-1] != (char)('H' | 0200))
! 172: break;
! 173: column -= 2;
! 174: if (c == curr[-2])
! 175: goto enter_boldface;
! 176: if (c == '_' || curr[-2] == '_')
! 177: goto enter_underline;
! 178: curr -= 2;
! 179: break;
! 180:
! 181: enter_boldface:
! 182: /*
! 183: * We have "X\bX" (including the current char).
! 184: * Switch into boldface mode.
! 185: */
! 186: column--;
! 187: if (column + bo_width + be_width + 1 >= sc_width)
! 188: /*
! 189: * Not enough room left on the screen to
! 190: * enter and exit boldface mode.
! 191: */
! 192: return (1);
! 193:
! 194: if (bo_width > 0 && curr > linebuf + 2
! 195: && curr[-3] == ' ') {
! 196: /*
! 197: * Special case for magic cookie terminals:
! 198: * if the previous char was a space, replace
! 199: * it with the "enter boldface" sequence.
! 200: */
! 201: curr[-3] = BO_CHAR;
! 202: column += bo_width-1;
! 203: } else {
! 204: curr[-1] = curr[-2];
! 205: curr[-2] = BO_CHAR;
! 206: column += bo_width;
! 207: curr++;
! 208: }
! 209: goto ln_bo_xb_case;
! 210:
! 211: enter_underline:
! 212: /*
! 213: * We have either "_\bX" or "X\b_" (including
! 214: * the current char). Switch into underline mode.
! 215: */
! 216: column--;
! 217: if (column + ul_width + ue_width + 1 >= sc_width)
! 218: /*
! 219: * Not enough room left on the screen to
! 220: * enter and exit underline mode.
! 221: */
! 222: return (1);
! 223:
! 224: if (ul_width > 0 &&
! 225: curr > linebuf + 2 && curr[-3] == ' ')
! 226: {
! 227: /*
! 228: * Special case for magic cookie terminals:
! 229: * if the previous char was a space, replace
! 230: * it with the "enter underline" sequence.
! 231: */
! 232: curr[-3] = UL_CHAR;
! 233: column += ul_width-1;
! 234: } else
! 235: {
! 236: curr[-1] = curr[-2];
! 237: curr[-2] = UL_CHAR;
! 238: column += ul_width;
! 239: curr++;
! 240: }
! 241: goto ln_ul_xb_case;
! 242: /*NOTREACHED*/
! 243: case LN_UL_XB:
! 244: /*
! 245: * Termination of a sequence "_\bX" or "X\b_".
! 246: */
! 247: if (c != '_' && curr[-2] != '_' && c == curr[-2])
! 248: {
! 249: /*
! 250: * We seem to have run on from underlining
! 251: * into boldfacing - this is a nasty fix, but
! 252: * until this whole routine is rewritten as a
! 253: * real DFA, ... well ...
! 254: */
! 255: curr[0] = curr[-2];
! 256: curr[-2] = UE_CHAR;
! 257: curr[-1] = BO_CHAR;
! 258: curr += 2; /* char & non-existent backspace */
! 259: ln_state = LN_BO_XB;
! 260: goto ln_bo_xb_case;
! 261: }
! 262: ln_ul_xb_case:
! 263: if (c == '_')
! 264: c = curr[-2];
! 265: curr -= 2;
! 266: ln_state = LN_UNDERLINE;
! 267: break;
! 268: case LN_BO_XB:
! 269: /*
! 270: * Termination of a sequnce "X\bX".
! 271: */
! 272: if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
! 273: {
! 274: /*
! 275: * We seem to have run on from
! 276: * boldfacing into underlining.
! 277: */
! 278: curr[0] = curr[-2];
! 279: curr[-2] = BE_CHAR;
! 280: curr[-1] = UL_CHAR;
! 281: curr += 2; /* char & non-existent backspace */
! 282: ln_state = LN_UL_XB;
! 283: goto ln_ul_xb_case;
! 284: }
! 285: ln_bo_xb_case:
! 286: curr -= 2;
! 287: ln_state = LN_BOLDFACE;
! 288: break;
! 289: case LN_UNDERLINE:
! 290: if (column + ue_width + bo_width + 1 + be_width >= sc_width)
! 291: /*
! 292: * We have just barely enough room to
! 293: * exit underline mode and handle a possible
! 294: * underline/boldface run on mixup.
! 295: */
! 296: return (1);
! 297: ln_state = LN_UL_X;
! 298: break;
! 299: case LN_BOLDFACE:
! 300: if (c == '\b')
! 301: {
! 302: ln_state = LN_BO_XB;
! 303: break;
! 304: }
! 305: if (column + be_width + ul_width + 1 + ue_width >= sc_width)
! 306: /*
! 307: * We have just barely enough room to
! 308: * exit underline mode and handle a possible
! 309: * underline/boldface run on mixup.
! 310: */
! 311: return (1);
! 312: ln_state = LN_BO_X;
! 313: break;
! 314: case LN_UL_X:
! 315: if (c == '\b')
! 316: ln_state = LN_UL_XB;
! 317: else
! 318: {
! 319: /*
! 320: * Exit underline mode.
! 321: * We have to shuffle the chars a bit
! 322: * to make this work.
! 323: */
! 324: curr[0] = curr[-1];
! 325: curr[-1] = UE_CHAR;
! 326: column += ue_width;
! 327: if (ue_width > 0 && curr[0] == ' ')
! 328: /*
! 329: * Another special case for magic
! 330: * cookie terminals: if the next
! 331: * char is a space, replace it
! 332: * with the "exit underline" sequence.
! 333: */
! 334: column--;
! 335: else
! 336: curr++;
! 337: ln_state = LN_NORMAL;
! 338: }
! 339: break;
! 340: case LN_BO_X:
! 341: if (c == '\b')
! 342: ln_state = LN_BO_XB;
! 343: else
! 344: {
! 345: /*
! 346: * Exit boldface mode.
! 347: * We have to shuffle the chars a bit
! 348: * to make this work.
! 349: */
! 350: curr[0] = curr[-1];
! 351: curr[-1] = BE_CHAR;
! 352: column += be_width;
! 353: if (be_width > 0 && curr[0] == ' ')
! 354: /*
! 355: * Another special case for magic
! 356: * cookie terminals: if the next
! 357: * char is a space, replace it
! 358: * with the "exit boldface" sequence.
! 359: */
! 360: column--;
! 361: else
! 362: curr++;
! 363: ln_state = LN_NORMAL;
! 364: }
! 365: break;
! 366: }
! 367: }
! 368:
! 369: if (c == '\t') {
! 370: /*
! 371: * Expand a tab into spaces.
! 372: */
! 373: do {
! 374: NEW_COLUMN(1);
! 375: } while ((column % tabstop) != 0);
! 376: *curr++ = '\t';
! 377: return (0);
! 378: }
! 379:
! 380: if (c == '\b') {
! 381: if (ln_state == LN_NORMAL)
! 382: NEW_COLUMN(2);
! 383: else
! 384: column--;
! 385: *curr++ = ('H' | 0200);
! 386: return(0);
! 387: }
! 388:
! 389: if (CONTROL_CHAR(c)) {
! 390: /*
! 391: * Put a "^X" into the buffer. The 0200 bit is used to tell
! 392: * put_line() to prefix the char with a ^. We don't actually
! 393: * put the ^ in the buffer because we sometimes need to move
! 394: * chars around, and such movement might separate the ^ from
! 395: * its following character.
! 396: */
! 397: NEW_COLUMN(2);
! 398: *curr++ = (CARAT_CHAR(c) | 0200);
! 399: return(0);
! 400: }
! 401:
! 402: /*
! 403: * Ordinary character. Just put it in the buffer.
! 404: */
! 405: NEW_COLUMN(1);
! 406: *curr++ = c;
! 407: return (0);
! 408: }
! 409:
! 410: /*
! 411: * Analogous to forw_line(), but deals with "raw lines":
! 412: * lines which are not split for screen width.
! 413: * {{ This is supposed to be more efficient than forw_line(). }}
! 414: */
! 415: off_t
! 416: forw_raw_line(curr_pos)
! 417: off_t curr_pos;
! 418: {
! 419: register char *p;
! 420: register int c;
! 421: off_t new_pos, ch_tell();
! 422:
! 423: if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
! 424: (c = ch_forw_get()) == EOI)
! 425: return (NULL_POSITION);
! 426:
! 427: p = linebuf;
! 428:
! 429: for (;;)
! 430: {
! 431: if (c == '\n' || c == EOI)
! 432: {
! 433: new_pos = ch_tell();
! 434: break;
! 435: }
! 436: if (p >= &linebuf[sizeof(linebuf)-1])
! 437: {
! 438: /*
! 439: * Overflowed the input buffer.
! 440: * Pretend the line ended here.
! 441: * {{ The line buffer is supposed to be big
! 442: * enough that this never happens. }}
! 443: */
! 444: new_pos = ch_tell() - 1;
! 445: break;
! 446: }
! 447: *p++ = c;
! 448: c = ch_forw_get();
! 449: }
! 450: *p = '\0';
! 451: line = linebuf;
! 452: return (new_pos);
! 453: }
! 454:
! 455: /*
! 456: * Analogous to back_line(), but deals with "raw lines".
! 457: * {{ This is supposed to be more efficient than back_line(). }}
! 458: */
! 459: off_t
! 460: back_raw_line(curr_pos)
! 461: off_t curr_pos;
! 462: {
! 463: register char *p;
! 464: register int c;
! 465: off_t new_pos, ch_tell();
! 466:
! 467: if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 ||
! 468: ch_seek(curr_pos-1))
! 469: return (NULL_POSITION);
! 470:
! 471: p = &linebuf[sizeof(linebuf)];
! 472: *--p = '\0';
! 473:
! 474: for (;;)
! 475: {
! 476: c = ch_back_get();
! 477: if (c == '\n')
! 478: {
! 479: /*
! 480: * This is the newline ending the previous line.
! 481: * We have hit the beginning of the line.
! 482: */
! 483: new_pos = ch_tell() + 1;
! 484: break;
! 485: }
! 486: if (c == EOI)
! 487: {
! 488: /*
! 489: * We have hit the beginning of the file.
! 490: * This must be the first line in the file.
! 491: * This must, of course, be the beginning of the line.
! 492: */
! 493: new_pos = (off_t)0;
! 494: break;
! 495: }
! 496: if (p <= linebuf)
! 497: {
! 498: /*
! 499: * Overflowed the input buffer.
! 500: * Pretend the line ended here.
! 501: */
! 502: new_pos = ch_tell() + 1;
! 503: break;
! 504: }
! 505: *--p = c;
! 506: }
! 507: line = p;
! 508: return (new_pos);
! 509: }