diff options
Diffstat (limited to 'usr.bin/more/line.c')
-rw-r--r-- | usr.bin/more/line.c | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/usr.bin/more/line.c b/usr.bin/more/line.c new file mode 100644 index 0000000..7634f35 --- /dev/null +++ b/usr.bin/more/line.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 1988 Mark Nudleman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)line.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * Routines to manipulate the "line buffer". + * The line buffer holds a line of output as it is being built + * in preparation for output to the screen. + * We keep track of the PRINTABLE length of the line as it is being built. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <less.h> + +static char linebuf[1024]; /* Buffer which holds the current output line */ +static char *curr; /* Pointer into linebuf */ +static int column; /* Printable length, accounting for + backspaces, etc. */ +/* + * A ridiculously complex state machine takes care of backspaces. The + * complexity arises from the attempt to deal with all cases, especially + * involving long lines with underlining, boldfacing or whatever. There + * are still some cases which will break it. + * + * There are four states: + * LN_NORMAL is the normal state (not in underline mode). + * LN_UNDERLINE means we are in underline mode. We expect to get + * either a sequence like "_\bX" or "X\b_" to continue + * underline mode, or anything else to end underline mode. + * LN_BOLDFACE means we are in boldface mode. We expect to get sequences + * like "X\bX\b...X\bX" to continue boldface mode, or anything + * else to end boldface mode. + * LN_UL_X means we are one character after LN_UNDERLINE + * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_"). + * LN_UL_XB means we are one character after LN_UL_X + * (we have gotten the backspace in "_\bX" or "X\b_"; + * we expect one more ordinary character, + * which will put us back in state LN_UNDERLINE). + * LN_BO_X means we are one character after LN_BOLDFACE + * (we have gotten the 'X' in "X\bX"). + * LN_BO_XB means we are one character after LN_BO_X + * (we have gotten the backspace in "X\bX"; + * we expect one more 'X' which will put us back + * in LN_BOLDFACE). + */ +static int ln_state; /* Currently in normal/underline/bold/etc mode? */ +#define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */ +#define LN_UNDERLINE 1 /* In underline, need next char */ +#define LN_UL_X 2 /* In underline, got char, need \b */ +#define LN_UL_XB 3 /* In underline, got char & \b, need one more */ +#define LN_BOLDFACE 4 /* In boldface, need next char */ +#define LN_BO_X 5 /* In boldface, got char, need \b */ +#define LN_BO_XB 6 /* In boldface, got char & \b, need same char */ + +char *line; /* Pointer to the current line. + Usually points to linebuf. */ + +extern int bs_mode; +extern int tabstop; +extern int bo_width, be_width; +extern int ul_width, ue_width; +extern int sc_width, sc_height; + +/* + * Rewind the line buffer. + */ +prewind() +{ + line = curr = linebuf; + ln_state = LN_NORMAL; + column = 0; +} + +/* + * Append a character to the line buffer. + * Expand tabs into spaces, handle underlining, boldfacing, etc. + * Returns 0 if ok, 1 if couldn't fit in buffer. + */ +#define NEW_COLUMN(addon) \ + if (column + addon + (ln_state ? ue_width : 0) > sc_width) \ + return(1); \ + else \ + column += addon + +pappend(c) + int c; +{ + if (c == '\0') { + /* + * Terminate any special modes, if necessary. + * Append a '\0' to the end of the line. + */ + switch (ln_state) { + case LN_UL_X: + curr[0] = curr[-1]; + curr[-1] = UE_CHAR; + curr++; + break; + case LN_BO_X: + curr[0] = curr[-1]; + curr[-1] = BE_CHAR; + curr++; + break; + case LN_UL_XB: + case LN_UNDERLINE: + *curr++ = UE_CHAR; + break; + case LN_BO_XB: + case LN_BOLDFACE: + *curr++ = BE_CHAR; + break; + } + ln_state = LN_NORMAL; + *curr = '\0'; + return(0); + } + + if (curr > linebuf + sizeof(linebuf) - 12) + /* + * Almost out of room in the line buffer. + * Don't take any chances. + * {{ Linebuf is supposed to be big enough that this + * will never happen, but may need to be made + * bigger for wide screens or lots of backspaces. }} + */ + return(1); + + if (!bs_mode) { + /* + * Advance the state machine. + */ + switch (ln_state) { + case LN_NORMAL: + if (curr <= linebuf + 1 + || curr[-1] != (char)('H' | 0200)) + break; + column -= 2; + if (c == curr[-2]) + goto enter_boldface; + if (c == '_' || curr[-2] == '_') + goto enter_underline; + curr -= 2; + break; + +enter_boldface: + /* + * We have "X\bX" (including the current char). + * Switch into boldface mode. + */ + column--; + if (column + bo_width + be_width + 1 >= sc_width) + /* + * Not enough room left on the screen to + * enter and exit boldface mode. + */ + return (1); + + if (bo_width > 0 && curr > linebuf + 2 + && curr[-3] == ' ') { + /* + * Special case for magic cookie terminals: + * if the previous char was a space, replace + * it with the "enter boldface" sequence. + */ + curr[-3] = BO_CHAR; + column += bo_width-1; + } else { + curr[-1] = curr[-2]; + curr[-2] = BO_CHAR; + column += bo_width; + curr++; + } + goto ln_bo_xb_case; + +enter_underline: + /* + * We have either "_\bX" or "X\b_" (including + * the current char). Switch into underline mode. + */ + column--; + if (column + ul_width + ue_width + 1 >= sc_width) + /* + * Not enough room left on the screen to + * enter and exit underline mode. + */ + return (1); + + if (ul_width > 0 && + curr > linebuf + 2 && curr[-3] == ' ') + { + /* + * Special case for magic cookie terminals: + * if the previous char was a space, replace + * it with the "enter underline" sequence. + */ + curr[-3] = UL_CHAR; + column += ul_width-1; + } else + { + curr[-1] = curr[-2]; + curr[-2] = UL_CHAR; + column += ul_width; + curr++; + } + goto ln_ul_xb_case; + /*NOTREACHED*/ + case LN_UL_XB: + /* + * Termination of a sequence "_\bX" or "X\b_". + */ + if (c != '_' && curr[-2] != '_' && c == curr[-2]) + { + /* + * We seem to have run on from underlining + * into boldfacing - this is a nasty fix, but + * until this whole routine is rewritten as a + * real DFA, ... well ... + */ + curr[0] = curr[-2]; + curr[-2] = UE_CHAR; + curr[-1] = BO_CHAR; + curr += 2; /* char & non-existent backspace */ + ln_state = LN_BO_XB; + goto ln_bo_xb_case; + } +ln_ul_xb_case: + if (c == '_') + c = curr[-2]; + curr -= 2; + ln_state = LN_UNDERLINE; + break; + case LN_BO_XB: + /* + * Termination of a sequnce "X\bX". + */ + if (c != curr[-2] && (c == '_' || curr[-2] == '_')) + { + /* + * We seem to have run on from + * boldfacing into underlining. + */ + curr[0] = curr[-2]; + curr[-2] = BE_CHAR; + curr[-1] = UL_CHAR; + curr += 2; /* char & non-existent backspace */ + ln_state = LN_UL_XB; + goto ln_ul_xb_case; + } +ln_bo_xb_case: + curr -= 2; + ln_state = LN_BOLDFACE; + break; + case LN_UNDERLINE: + if (column + ue_width + bo_width + 1 + be_width >= sc_width) + /* + * We have just barely enough room to + * exit underline mode and handle a possible + * underline/boldface run on mixup. + */ + return (1); + ln_state = LN_UL_X; + break; + case LN_BOLDFACE: + if (c == '\b') + { + ln_state = LN_BO_XB; + break; + } + if (column + be_width + ul_width + 1 + ue_width >= sc_width) + /* + * We have just barely enough room to + * exit underline mode and handle a possible + * underline/boldface run on mixup. + */ + return (1); + ln_state = LN_BO_X; + break; + case LN_UL_X: + if (c == '\b') + ln_state = LN_UL_XB; + else + { + /* + * Exit underline mode. + * We have to shuffle the chars a bit + * to make this work. + */ + curr[0] = curr[-1]; + curr[-1] = UE_CHAR; + column += ue_width; + if (ue_width > 0 && curr[0] == ' ') + /* + * Another special case for magic + * cookie terminals: if the next + * char is a space, replace it + * with the "exit underline" sequence. + */ + column--; + else + curr++; + ln_state = LN_NORMAL; + } + break; + case LN_BO_X: + if (c == '\b') + ln_state = LN_BO_XB; + else + { + /* + * Exit boldface mode. + * We have to shuffle the chars a bit + * to make this work. + */ + curr[0] = curr[-1]; + curr[-1] = BE_CHAR; + column += be_width; + if (be_width > 0 && curr[0] == ' ') + /* + * Another special case for magic + * cookie terminals: if the next + * char is a space, replace it + * with the "exit boldface" sequence. + */ + column--; + else + curr++; + ln_state = LN_NORMAL; + } + break; + } + } + + if (c == '\t') { + /* + * Expand a tab into spaces. + */ + do { + NEW_COLUMN(1); + } while ((column % tabstop) != 0); + *curr++ = '\t'; + return (0); + } + + if (c == '\b') { + if (ln_state == LN_NORMAL) + NEW_COLUMN(2); + else + column--; + *curr++ = ('H' | 0200); + return(0); + } + + if (CONTROL_CHAR(c)) { + /* + * Put a "^X" into the buffer. The 0200 bit is used to tell + * put_line() to prefix the char with a ^. We don't actually + * put the ^ in the buffer because we sometimes need to move + * chars around, and such movement might separate the ^ from + * its following character. + */ + NEW_COLUMN(2); + *curr++ = (CARAT_CHAR(c) | 0200); + return(0); + } + + /* + * Ordinary character. Just put it in the buffer. + */ + NEW_COLUMN(1); + *curr++ = c; + return (0); +} + +/* + * Analogous to forw_line(), but deals with "raw lines": + * lines which are not split for screen width. + * {{ This is supposed to be more efficient than forw_line(). }} + */ +off_t +forw_raw_line(curr_pos) + off_t curr_pos; +{ + register char *p; + register int c; + off_t new_pos, ch_tell(); + + if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || + (c = ch_forw_get()) == EOI) + return (NULL_POSITION); + + p = linebuf; + + for (;;) + { + if (c == '\n' || c == EOI) + { + new_pos = ch_tell(); + break; + } + if (p >= &linebuf[sizeof(linebuf)-1]) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + * {{ The line buffer is supposed to be big + * enough that this never happens. }} + */ + new_pos = ch_tell() - 1; + break; + } + *p++ = c; + c = ch_forw_get(); + } + *p = '\0'; + line = linebuf; + return (new_pos); +} + +/* + * Analogous to back_line(), but deals with "raw lines". + * {{ This is supposed to be more efficient than back_line(). }} + */ +off_t +back_raw_line(curr_pos) + off_t curr_pos; +{ + register char *p; + register int c; + off_t new_pos, ch_tell(); + + if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 || + ch_seek(curr_pos-1)) + return (NULL_POSITION); + + p = &linebuf[sizeof(linebuf)]; + *--p = '\0'; + + for (;;) + { + c = ch_back_get(); + if (c == '\n') + { + /* + * This is the newline ending the previous line. + * We have hit the beginning of the line. + */ + new_pos = ch_tell() + 1; + break; + } + if (c == EOI) + { + /* + * We have hit the beginning of the file. + * This must be the first line in the file. + * This must, of course, be the beginning of the line. + */ + new_pos = (off_t)0; + break; + } + if (p <= linebuf) + { + /* + * Overflowed the input buffer. + * Pretend the line ended here. + */ + new_pos = ch_tell() + 1; + break; + } + *--p = c; + } + line = p; + return (new_pos); +} |