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
- in line 55,
65,
124,
140-145(2),
159,
179,
187,
203-204(2),
216,
223,
239,
247
curr
defined in line
11; used 31 times
- in line 53,
80-94(7),
112-116(4),
132(2),
139-146(5),
154-155(2),
177-180(4),
189,
205,
217,
224,
240,
248
line
defined in line
39; used 15 times
Defined macros
UL_X
defined in line
36; used 1 times
UL_XB
defined in line
37; used 1 times