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_SPECIAL mode. The complexity arises from the attempt
17: * to deal with all cases, especially involving long lines with underlining,
18: * boldfacing or whatever. There are still some cases which will break it.
19: *
20: * There are four states:
21: * LN_NORMAL is the normal state (not in underline mode).
22: * LN_UNDERLINE 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 anything else to end underline mode.
25: * LN_BOLDFACE means we are in boldface mode. We expect to get sequences
26: * like "X\bX\b...X\bX" to continue boldface mode, or anything
27: * else to end boldface mode.
28: * LN_UL_X means we are one character after LN_UNDERLINE
29: * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
30: * LN_UL_XB means we are one character after LN_UL_X
31: * (we have gotten the backspace in "_\bX" or "X\b_";
32: * we expect one more ordinary character,
33: * which will put us back in state LN_UNDERLINE).
34: * LN_BO_X means we are one character after LN_BOLDFACE
35: * (we have gotten the 'X' in "X\bX").
36: * LN_BO_XB means we are one character after LN_BO_X
37: * (we have gotten the backspace in "X\bX";
38: * we expect one more 'X' which will put us back
39: * in LN_BOLDFACE).
40: */
41: static int ln_state; /* Currently in normal/underline/bold/etc mode? */
42: #define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */
43: #define LN_UNDERLINE 1 /* In underline, need next char */
44: #define LN_UL_X 2 /* In underline, got char, need \b */
45: #define LN_UL_XB 3 /* In underline, got char & \b, need one more */
46: #define LN_BOLDFACE 4 /* In boldface, need next char */
47: #define LN_BO_X 5 /* In boldface, got char, need \b */
48: #define LN_BO_XB 6 /* In boldface, got char & \b, need same char */
49:
50: public char *line; /* Pointer to the current line.
51: Usually points to linebuf. */
52:
53: extern int bs_mode;
54: extern int tabstop;
55: extern int bo_width, be_width;
56: extern int ul_width, ue_width;
57: extern int sc_width, sc_height;
58:
59: /*
60: * Rewind the line buffer.
61: */
62: public void
63: prewind()
64: {
65: line = curr = linebuf;
66: ln_state = LN_NORMAL;
67: column = 0;
68: }
69:
70: /*
71: * Append a character to the line buffer.
72: * Expand tabs into spaces, handle underlining, boldfacing, etc.
73: * Returns 0 if ok, 1 if couldn't fit in buffer.
74: */
75:
76: #define NEW_COLUMN(newcol) if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
77: return (1); else column = (newcol)
78:
79: public int
80: pappend(c)
81: int c;
82: {
83: if (c == '\0')
84: {
85: /*
86: * Terminate any special modes, if necessary.
87: * Append a '\0' to the end of the line.
88: */
89: switch (ln_state)
90: {
91: case LN_UL_X:
92: curr[0] = curr[-1];
93: curr[-1] = UE_CHAR;
94: curr++;
95: break;
96: case LN_BO_X:
97: curr[0] = curr[-1];
98: curr[-1] = BE_CHAR;
99: curr++;
100: break;
101: case LN_UL_XB:
102: case LN_UNDERLINE:
103: *curr++ = UE_CHAR;
104: break;
105: case LN_BO_XB:
106: case LN_BOLDFACE:
107: *curr++ = BE_CHAR;
108: break;
109: }
110: ln_state = LN_NORMAL;
111: *curr = '\0';
112: return (0);
113: }
114:
115: if (curr > linebuf + sizeof(linebuf) - 12)
116: /*
117: * Almost out of room in the line buffer.
118: * Don't take any chances.
119: * {{ Linebuf is supposed to be big enough that this
120: * will never happen, but may need to be made
121: * bigger for wide screens or lots of backspaces. }}
122: */
123: return (1);
124:
125: if (bs_mode == BS_SPECIAL)
126: {
127: /*
128: * Advance the state machine.
129: */
130: switch (ln_state)
131: {
132: case LN_NORMAL:
133: if (curr <= linebuf + 1 || curr[-1] != '\b')
134: break;
135:
136: if (c == curr[-2])
137: goto enter_boldface;
138: if (c == '_' || curr[-2] == '_')
139: goto enter_underline;
140: curr -= 2;
141: break;
142:
143: enter_boldface:
144: /*
145: * We have "X\bX" (including the current char).
146: * Switch into boldface mode.
147: */
148: if (column + bo_width + be_width + 1 >= sc_width)
149: /*
150: * Not enough room left on the screen to
151: * enter and exit boldface mode.
152: */
153: return (1);
154:
155: if (bo_width > 0 &&
156: curr > linebuf + 2 && curr[-3] == ' ')
157: {
158: /*
159: * Special case for magic cookie terminals:
160: * if the previous char was a space, replace
161: * it with the "enter boldface" sequence.
162: */
163: curr[-3] = BO_CHAR;
164: column += bo_width-1;
165: } else
166: {
167: curr[-1] = curr[-2];
168: curr[-2] = BO_CHAR;
169: column += bo_width;
170: curr++;
171: }
172: goto ln_bo_xb_case;
173:
174: enter_underline:
175: /*
176: * We have either "_\bX" or "X\b_" (including
177: * the current char). Switch into underline mode.
178: */
179: if (column + ul_width + ue_width + 1 >= sc_width)
180: /*
181: * Not enough room left on the screen to
182: * enter and exit underline mode.
183: */
184: return (1);
185:
186: if (ul_width > 0 &&
187: curr > linebuf + 2 && curr[-3] == ' ')
188: {
189: /*
190: * Special case for magic cookie terminals:
191: * if the previous char was a space, replace
192: * it with the "enter underline" sequence.
193: */
194: curr[-3] = UL_CHAR;
195: column += ul_width-1;
196: } else
197: {
198: curr[-1] = curr[-2];
199: curr[-2] = UL_CHAR;
200: column += ul_width;
201: curr++;
202: }
203: goto ln_ul_xb_case;
204: /*NOTREACHED*/
205: case LN_UL_XB:
206: /*
207: * Termination of a sequence "_\bX" or "X\b_".
208: */
209: if (c != '_' && curr[-2] != '_' && c == curr[-2])
210: {
211: /*
212: * We seem to have run on from underlining
213: * into boldfacing - this is a nasty fix, but
214: * until this whole routine is rewritten as a
215: * real DFA, ... well ...
216: */
217: curr[0] = curr[-2];
218: curr[-2] = UE_CHAR;
219: curr[-1] = BO_CHAR;
220: curr += 2; /* char & non-existent backspace */
221: ln_state = LN_BO_XB;
222: goto ln_bo_xb_case;
223: }
224: ln_ul_xb_case:
225: if (c == '_')
226: c = curr[-2];
227: curr -= 2;
228: ln_state = LN_UNDERLINE;
229: break;
230: case LN_BO_XB:
231: /*
232: * Termination of a sequnce "X\bX".
233: */
234: if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
235: {
236: /*
237: * We seem to have run on from
238: * boldfacing into underlining.
239: */
240: curr[0] = curr[-2];
241: curr[-2] = BE_CHAR;
242: curr[-1] = UL_CHAR;
243: curr += 2; /* char & non-existent backspace */
244: ln_state = LN_UL_XB;
245: goto ln_ul_xb_case;
246: }
247: ln_bo_xb_case:
248: curr -= 2;
249: ln_state = LN_BOLDFACE;
250: break;
251: case LN_UNDERLINE:
252: if (column + ue_width + bo_width + 1 + be_width >= sc_width)
253: /*
254: * We have just barely enough room to
255: * exit underline mode and handle a possible
256: * underline/boldface run on mixup.
257: */
258: return (1);
259: ln_state = LN_UL_X;
260: break;
261: case LN_BOLDFACE:
262: if (c == '\b')
263: {
264: ln_state = LN_BO_XB;
265: break;
266: }
267: if (column + be_width + ul_width + 1 + ue_width >= sc_width)
268: /*
269: * We have just barely enough room to
270: * exit underline mode and handle a possible
271: * underline/boldface run on mixup.
272: */
273: return (1);
274: ln_state = LN_BO_X;
275: break;
276: case LN_UL_X:
277: if (c == '\b')
278: ln_state = LN_UL_XB;
279: else
280: {
281: /*
282: * Exit underline mode.
283: * We have to shuffle the chars a bit
284: * to make this work.
285: */
286: curr[0] = curr[-1];
287: curr[-1] = UE_CHAR;
288: column += ue_width;
289: if (ue_width > 0 && curr[0] == ' ')
290: /*
291: * Another special case for magic
292: * cookie terminals: if the next
293: * char is a space, replace it
294: * with the "exit underline" sequence.
295: */
296: column--;
297: else
298: curr++;
299: ln_state = LN_NORMAL;
300: }
301: break;
302: case LN_BO_X:
303: if (c == '\b')
304: ln_state = LN_BO_XB;
305: else
306: {
307: /*
308: * Exit boldface mode.
309: * We have to shuffle the chars a bit
310: * to make this work.
311: */
312: curr[0] = curr[-1];
313: curr[-1] = BE_CHAR;
314: column += be_width;
315: if (be_width > 0 && curr[0] == ' ')
316: /*
317: * Another special case for magic
318: * cookie terminals: if the next
319: * char is a space, replace it
320: * with the "exit boldface" sequence.
321: */
322: column--;
323: else
324: curr++;
325: ln_state = LN_NORMAL;
326: }
327: break;
328: }
329: }
330:
331: if (c == '\t')
332: {
333: /*
334: * Expand a tab into spaces.
335: */
336: do
337: {
338: NEW_COLUMN(column+1);
339: } while ((column % tabstop) != 0);
340: *curr++ = '\t';
341: return (0);
342: }
343:
344: if (c == '\b')
345: {
346: if (bs_mode == BS_CONTROL)
347: {
348: /*
349: * Treat backspace as a control char: output "^H".
350: */
351: NEW_COLUMN(column+2);
352: *curr++ = ('H' | 0200);
353: } else
354: {
355: /*
356: * Output a real backspace.
357: */
358: column--;
359: *curr++ = '\b';
360: }
361: return (0);
362: }
363:
364: if (control_char(c))
365: {
366: /*
367: * Put a "^X" into the buffer.
368: * The 0200 bit is used to tell put_line() to prefix
369: * the char with a ^. We don't actually put the ^
370: * in the buffer because we sometimes need to move
371: * chars around, and such movement might separate
372: * the ^ from its following character.
373: * {{ This should be redone so that we can use an
374: * 8 bit (e.g. international) character set. }}
375: */
376: NEW_COLUMN(column+2);
377: *curr++ = (carat_char(c) | 0200);
378: return (0);
379: }
380:
381: /*
382: * Ordinary character. Just put it in the buffer.
383: */
384: NEW_COLUMN(column+1);
385: *curr++ = c;
386: return (0);
387: }
388:
389: /*
390: * Analogous to forw_line(), but deals with "raw lines":
391: * lines which are not split for screen width.
392: * {{ This is supposed to be more efficient than forw_line(). }}
393: */
394: public POSITION
395: forw_raw_line(curr_pos)
396: POSITION curr_pos;
397: {
398: register char *p;
399: register int c;
400: POSITION new_pos;
401:
402: if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
403: (c = ch_forw_get()) == EOF)
404: return (NULL_POSITION);
405:
406: p = linebuf;
407:
408: for (;;)
409: {
410: if (c == '\n' || c == EOF)
411: {
412: new_pos = ch_tell();
413: break;
414: }
415: if (p >= &linebuf[sizeof(linebuf)-1])
416: {
417: /*
418: * Overflowed the input buffer.
419: * Pretend the line ended here.
420: * {{ The line buffer is supposed to be big
421: * enough that this never happens. }}
422: */
423: new_pos = ch_tell() - 1;
424: break;
425: }
426: *p++ = c;
427: c = ch_forw_get();
428: }
429: *p = '\0';
430: line = linebuf;
431: return (new_pos);
432: }
433:
434: /*
435: * Analogous to back_line(), but deals with "raw lines".
436: * {{ This is supposed to be more efficient than back_line(). }}
437: */
438: public POSITION
439: back_raw_line(curr_pos)
440: POSITION curr_pos;
441: {
442: register char *p;
443: register int c;
444: POSITION new_pos;
445:
446: if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
447: ch_seek(curr_pos-1))
448: return (NULL_POSITION);
449:
450: p = &linebuf[sizeof(linebuf)];
451: *--p = '\0';
452:
453: for (;;)
454: {
455: c = ch_back_get();
456: if (c == '\n')
457: {
458: /*
459: * This is the newline ending the previous line.
460: * We have hit the beginning of the line.
461: */
462: new_pos = ch_tell() + 1;
463: break;
464: }
465: if (c == EOF)
466: {
467: /*
468: * We have hit the beginning of the file.
469: * This must be the first line in the file.
470: * This must, of course, be the beginning of the line.
471: */
472: new_pos = (POSITION)0;
473: break;
474: }
475: if (p <= linebuf)
476: {
477: /*
478: * Overflowed the input buffer.
479: * Pretend the line ended here.
480: */
481: new_pos = ch_tell() + 1;
482: break;
483: }
484: *--p = c;
485: }
486: line = p;
487: return (new_pos);
488: }
Defined functions
Defined variables
column
defined in line
12; used 20 times
- in line 67,
77,
148,
164-169(2),
179,
195-200(2),
252,
267,
288,
296,
314,
322,
338-339(2),
351,
358,
376,
384
curr
defined in line
11; used 64 times
- in line 65,
92-115(12),
133-140(5),
156(2),
163-170(5),
187(2),
194-201(5),
209(2),
217-220(5),
226-227(2),
234(2),
240-248(6),
286-289(4),
298,
312-315(4),
324,
340,
352,
359,
377,
385
line
defined in line
50; used 13 times
ln_state
defined in line
41; used 16 times
- in line 66,
76,
89,
110,
130,
221,
228,
244-249(2),
259-264(2),
274-278(2),
299-304(2),
325
Defined macros