Annotation of src/usr.bin/more/line.c, Revision 1.1.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: }