Annotation of src/usr.bin/less/line.c, Revision 1.7
1.1 etheisen 1: /*
1.5 millert 2: * Copyright (C) 1984-2002 Mark Nudelman
1.1 etheisen 3: *
1.5 millert 4: * You may distribute under the terms of either the GNU General Public
5: * License or the Less License, as specified in the README file.
1.1 etheisen 6: *
1.5 millert 7: * For more information about less, or for information on how to
8: * contact the author, see the README file.
1.1 etheisen 9: */
10:
11:
12: /*
13: * Routines to manipulate the "line buffer".
14: * The line buffer holds a line of output as it is being built
15: * in preparation for output to the screen.
16: */
17:
18: #include "less.h"
19:
1.5 millert 20: #define IS_CONT(c) (((c) & 0xC0) == 0x80)
21:
22: public char *linebuf = NULL; /* Buffer which holds the current output line */
23: static char *attr = NULL; /* Extension of linebuf to hold attributes */
24: public int size_linebuf = 0; /* Size of line buffer (and attr buffer) */
25:
26: public int cshift; /* Current left-shift of output line buffer */
27: public int hshift; /* Desired left-shift of output line buffer */
28: public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */
29: public int ntabstops = 1; /* Number of tabstops */
30: public int tabdefault = 8; /* Default repeated tabstops */
1.1 etheisen 31:
32: static int curr; /* Index into linebuf */
33: static int column; /* Printable length, accounting for
34: backspaces, etc. */
35: static int overstrike; /* Next char should overstrike previous char */
1.5 millert 36: static int last_overstrike = AT_NORMAL;
1.1 etheisen 37: static int is_null_line; /* There is no current line */
1.5 millert 38: static int lmargin; /* Left margin */
39: static int hilites; /* Number of hilites in this line */
1.1 etheisen 40: static char pendc;
41: static POSITION pendpos;
1.5 millert 42: static char *end_ansi_chars;
1.1 etheisen 43:
44: static int do_append();
45:
46: extern int bs_mode;
47: extern int linenums;
48: extern int ctldisp;
49: extern int twiddle;
50: extern int binattr;
1.5 millert 51: extern int status_col;
1.1 etheisen 52: extern int auto_wrap, ignaw;
53: extern int bo_s_width, bo_e_width;
54: extern int ul_s_width, ul_e_width;
55: extern int bl_s_width, bl_e_width;
56: extern int so_s_width, so_e_width;
57: extern int sc_width, sc_height;
1.5 millert 58: extern int utf_mode;
59: extern POSITION start_attnpos;
60: extern POSITION end_attnpos;
61:
62: /*
63: * Initialize from environment variables.
64: */
65: public void
66: init_line()
67: {
68: end_ansi_chars = lgetenv("LESSANSIENDCHARS");
69: if (end_ansi_chars == NULL || *end_ansi_chars == '\0')
70: end_ansi_chars = "m";
71: linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
72: attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char));
73: size_linebuf = LINEBUF_SIZE;
74: }
75:
76: /*
77: * Expand the line buffer.
78: */
79: static int
80: expand_linebuf()
81: {
1.7 ! millert 82: int new_size = size_linebuf * 2;
! 83: char *new_buf = (char *) realloc(linebuf, new_size);
! 84: char *new_attr = (char *) realloc(attr, new_size);
1.5 millert 85: if (new_buf == NULL || new_attr == NULL)
86: {
87: if (new_attr != NULL)
88: free(new_attr);
89: if (new_buf != NULL)
90: free(new_buf);
91: return 1;
92: }
1.7 ! millert 93: memset(new_buf + size_linebuf, 0, new_size - size_linebuf);
! 94: memset(new_attr + size_linebuf, 0, new_size - size_linebuf);
1.5 millert 95: linebuf = new_buf;
96: attr = new_attr;
97: size_linebuf = new_size;
98: return 0;
99: }
1.1 etheisen 100:
101: /*
102: * Rewind the line buffer.
103: */
104: public void
105: prewind()
106: {
107: curr = 0;
108: column = 0;
109: overstrike = 0;
110: is_null_line = 0;
111: pendc = '\0';
1.5 millert 112: lmargin = 0;
113: if (status_col)
114: lmargin += 1;
115: #if HILITE_SEARCH
116: hilites = 0;
117: #endif
1.1 etheisen 118: }
119:
120: /*
121: * Insert the line number (of the given position) into the line buffer.
122: */
123: public void
124: plinenum(pos)
125: POSITION pos;
126: {
1.5 millert 127: register LINENUM linenum = 0;
128: register int i;
129:
130: if (linenums == OPT_ONPLUS)
131: {
132: /*
133: * Get the line number and put it in the current line.
134: * {{ Note: since find_linenum calls forw_raw_line,
135: * it may seek in the input file, requiring the caller
136: * of plinenum to re-seek if necessary. }}
137: * {{ Since forw_raw_line modifies linebuf, we must
138: * do this first, before storing anything in linebuf. }}
139: */
140: linenum = find_linenum(pos);
141: }
1.1 etheisen 142:
143: /*
1.5 millert 144: * Display a status column if the -J option is set.
1.1 etheisen 145: */
1.5 millert 146: if (status_col)
147: {
148: linebuf[curr] = ' ';
149: if (start_attnpos != NULL_POSITION &&
150: pos >= start_attnpos && pos < end_attnpos)
151: attr[curr] = AT_STANDOUT;
152: else
153: attr[curr] = 0;
154: curr++;
155: column++;
156: }
1.1 etheisen 157: /*
1.5 millert 158: * Display the line number at the start of each line
159: * if the -N option is set.
1.1 etheisen 160: */
1.5 millert 161: if (linenums == OPT_ONPLUS)
162: {
163: char buf[INT_STRLEN_BOUND(pos) + 2];
164: int n;
1.1 etheisen 165:
1.5 millert 166: linenumtoa(linenum, buf, sizeof(buf));
167: n = strlen(buf);
168: if (n < MIN_LINENUM_WIDTH)
169: n = MIN_LINENUM_WIDTH;
1.6 millert 170: snprintf(linebuf+curr, size_linebuf-curr, "%*s ", n, buf);
1.5 millert 171: n++; /* One space after the line number. */
172: for (i = 0; i < n; i++)
173: attr[curr+i] = AT_NORMAL;
174: curr += n;
175: column += n;
176: lmargin += n;
177: }
1.1 etheisen 178:
179: /*
1.5 millert 180: * Append enough spaces to bring us to the lmargin.
1.1 etheisen 181: */
1.5 millert 182: while (column < lmargin)
1.1 etheisen 183: {
184: linebuf[curr] = ' ';
185: attr[curr++] = AT_NORMAL;
186: column++;
1.5 millert 187: }
188: }
189:
190: /*
191: * Determine how many characters are required to shift N columns.
192: */
193: static int
194: shift_chars(s, len)
195: char *s;
196: int len;
197: {
198: char *p = s;
199:
200: /*
201: * Each char counts for one column, except ANSI color escape
202: * sequences use no columns since they don't move the cursor.
203: */
204: while (*p != '\0' && len > 0)
205: {
206: if (*p++ != ESC)
207: {
208: len--;
209: } else
210: {
211: while (*p != '\0')
212: {
213: if (is_ansi_end(*p++))
214: break;
215: }
216: }
217: }
218: return (p - s);
219: }
220:
221: /*
222: * Determine how many characters are required to shift N columns (UTF version).
223: * {{ FIXME: what about color escape sequences in UTF mode? }}
224: */
225: static int
226: utf_shift_chars(s, len)
227: char *s;
228: int len;
229: {
230: int ulen = 0;
231:
232: while (*s != '\0' && len > 0)
233: {
234: if (!IS_CONT(*s))
235: len--;
236: s++;
237: ulen++;
238: }
239: while (IS_CONT(*s))
240: {
241: s++;
242: ulen++;
243: }
244: return (ulen);
245: }
246:
247: /*
248: * Shift the input line left.
249: * This means discarding N printable chars at the start of the buffer.
250: */
251: static void
252: pshift(shift)
253: int shift;
254: {
255: int i;
256: int nchars;
257:
258: if (shift > column - lmargin)
259: shift = column - lmargin;
260: if (shift > curr - lmargin)
261: shift = curr - lmargin;
262:
263: if (utf_mode)
264: nchars = utf_shift_chars(linebuf + lmargin, shift);
265: else
266: nchars = shift_chars(linebuf + lmargin, shift);
267: if (nchars > curr)
268: nchars = curr;
269: for (i = 0; i < curr - nchars; i++)
270: {
271: linebuf[lmargin + i] = linebuf[lmargin + i + nchars];
272: attr[lmargin + i] = attr[lmargin + i + nchars];
273: }
274: curr -= nchars;
275: column -= shift;
276: cshift += shift;
1.1 etheisen 277: }
278:
279: /*
280: * Return the printing width of the start (enter) sequence
281: * for a given character attribute.
282: */
1.5 millert 283: static int
1.1 etheisen 284: attr_swidth(a)
285: int a;
286: {
287: switch (a)
288: {
289: case AT_BOLD: return (bo_s_width);
290: case AT_UNDERLINE: return (ul_s_width);
291: case AT_BLINK: return (bl_s_width);
292: case AT_STANDOUT: return (so_s_width);
293: }
294: return (0);
295: }
296:
297: /*
298: * Return the printing width of the end (exit) sequence
299: * for a given character attribute.
300: */
1.5 millert 301: static int
1.1 etheisen 302: attr_ewidth(a)
303: int a;
304: {
305: switch (a)
306: {
307: case AT_BOLD: return (bo_e_width);
308: case AT_UNDERLINE: return (ul_e_width);
309: case AT_BLINK: return (bl_e_width);
310: case AT_STANDOUT: return (so_e_width);
311: }
312: return (0);
313: }
314:
315: /*
316: * Return the printing width of a given character and attribute,
317: * if the character were added to the current position in the line buffer.
318: * Adding a character with a given attribute may cause an enter or exit
319: * attribute sequence to be inserted, so this must be taken into account.
320: */
321: static int
322: pwidth(c, a)
323: int c;
324: int a;
325: {
1.5 millert 326: register int w;
327:
328: if (utf_mode && IS_CONT(c))
329: return (0);
1.1 etheisen 330:
331: if (c == '\b')
332: /*
333: * Backspace moves backwards one position.
334: */
335: return (-1);
336:
337: if (control_char(c))
338: /*
339: * Control characters do unpredicatable things,
340: * so we don't even try to guess; say it doesn't move.
341: * This can only happen if the -r flag is in effect.
342: */
343: return (0);
344:
345: /*
346: * Other characters take one space,
347: * plus the width of any attribute enter/exit sequence.
348: */
349: w = 1;
350: if (curr > 0 && attr[curr-1] != a)
351: w += attr_ewidth(attr[curr-1]);
352: if (a && (curr == 0 || attr[curr-1] != a))
353: w += attr_swidth(a);
354: return (w);
355: }
356:
357: /*
358: * Delete the previous character in the line buffer.
359: */
360: static void
361: backc()
362: {
363: curr--;
364: column -= pwidth(linebuf[curr], attr[curr]);
365: }
366:
367: /*
1.5 millert 368: * Are we currently within a recognized ANSI escape sequence?
369: */
370: static int
371: in_ansi_esc_seq()
372: {
373: int i;
374:
375: /*
376: * Search backwards for either an ESC (which means we ARE in a seq);
377: * or an end char (which means we're NOT in a seq).
378: */
379: for (i = curr-1; i >= 0; i--)
380: {
381: if (linebuf[i] == ESC)
382: return (1);
383: if (is_ansi_end(linebuf[i]))
384: return (0);
385: }
386: return (0);
387: }
388:
389: /*
390: * Is a character the end of an ANSI escape sequence?
391: */
392: public int
393: is_ansi_end(c)
394: char c;
395: {
396: return (strchr(end_ansi_chars, c) != NULL);
397: }
398:
399: /*
1.1 etheisen 400: * Append a character and attribute to the line buffer.
401: */
1.5 millert 402: #define STORE_CHAR(c,a,pos) \
403: do { if (store_char((c),(a),(pos))) return (1); else curr++; } while (0)
404:
1.1 etheisen 405: static int
1.5 millert 406: store_char(c, a, pos)
1.1 etheisen 407: int c;
408: int a;
409: POSITION pos;
410: {
1.5 millert 411: register int w;
1.1 etheisen 412:
1.5 millert 413: if (a != AT_NORMAL)
414: last_overstrike = a;
1.1 etheisen 415: #if HILITE_SEARCH
416: if (is_hilited(pos, pos+1, 0))
1.5 millert 417: {
1.1 etheisen 418: /*
419: * This character should be highlighted.
420: * Override the attribute passed in.
421: */
422: a = AT_STANDOUT;
1.5 millert 423: hilites++;
424: }
1.1 etheisen 425: #endif
1.5 millert 426: if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq())
427: w = 0;
428: else
429: w = pwidth(c, a);
430: if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width)
1.1 etheisen 431: /*
432: * Won't fit on screen.
433: */
434: return (1);
435:
1.5 millert 436: if (curr >= size_linebuf-2)
437: {
1.1 etheisen 438: /*
439: * Won't fit in line buffer.
1.5 millert 440: * Try to expand it.
1.1 etheisen 441: */
1.5 millert 442: if (expand_linebuf())
443: return (1);
444: }
1.1 etheisen 445:
446: /*
447: * Special handling for "magic cookie" terminals.
448: * If an attribute enter/exit sequence has a printing width > 0,
449: * and the sequence is adjacent to a space, delete the space.
450: * We just mark the space as invisible, to avoid having too
451: * many spaces deleted.
452: * {{ Note that even if the attribute width is > 1, we
453: * delete only one space. It's not worth trying to do more.
454: * It's hardly worth doing this much. }}
455: */
456: if (curr > 0 && a != AT_NORMAL &&
457: linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
458: attr_swidth(a) > 0)
459: {
460: /*
461: * We are about to append an enter-attribute sequence
462: * just after a space. Delete the space.
463: */
464: attr[curr-1] = AT_INVIS;
465: column--;
466: } else if (curr > 0 && attr[curr-1] != AT_NORMAL &&
467: attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
468: attr_ewidth(attr[curr-1]) > 0)
469: {
470: /*
471: * We are about to append a space just after an
472: * exit-attribute sequence. Delete the space.
473: */
474: a = AT_INVIS;
475: column--;
476: }
477: /* End of magic cookie handling. */
478:
479: linebuf[curr] = c;
480: attr[curr] = a;
481: column += w;
482: return (0);
483: }
484:
485: /*
1.5 millert 486: * Append a tab to the line buffer.
487: * Store spaces to represent the tab.
488: */
489: #define STORE_TAB(a,pos) \
490: do { if (store_tab((a),(pos))) return (1); } while (0)
491:
492: static int
493: store_tab(attr, pos)
494: int attr;
495: POSITION pos;
496: {
497: int to_tab = column + cshift - lmargin;
498: int i;
499:
500: if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1])
501: to_tab = tabdefault -
502: ((to_tab - tabstops[ntabstops-1]) % tabdefault);
503: else
504: {
505: for (i = ntabstops - 2; i >= 0; i--)
506: if (to_tab >= tabstops[i])
507: break;
508: to_tab = tabstops[i+1] - to_tab;
509: }
510:
511: do {
512: STORE_CHAR(' ', attr, pos);
513: } while (--to_tab > 0);
514: return 0;
515: }
516:
517: /*
1.1 etheisen 518: * Append a character to the line buffer.
519: * Expand tabs into spaces, handle underlining, boldfacing, etc.
520: * Returns 0 if ok, 1 if couldn't fit in buffer.
521: */
522: public int
523: pappend(c, pos)
1.5 millert 524: register int c;
1.1 etheisen 525: POSITION pos;
526: {
1.5 millert 527: int r;
528:
1.1 etheisen 529: if (pendc)
530: {
531: if (do_append(pendc, pendpos))
532: /*
533: * Oops. We've probably lost the char which
534: * was in pendc, since caller won't back up.
535: */
536: return (1);
537: pendc = '\0';
538: }
539:
540: if (c == '\r' && bs_mode == BS_SPECIAL)
541: {
542: /*
543: * Don't put the CR into the buffer until we see
544: * the next char. If the next char is a newline,
545: * discard the CR.
546: */
547: pendc = c;
548: pendpos = pos;
549: return (0);
550: }
551:
1.5 millert 552: r = do_append(c, pos);
553: /*
554: * If we need to shift the line, do it.
555: * But wait until we get to at least the middle of the screen,
556: * so shifting it doesn't affect the chars we're currently
557: * pappending. (Bold & underline can get messed up otherwise.)
558: */
559: if (cshift < hshift && column > sc_width / 2)
560: {
561: linebuf[curr] = '\0';
562: pshift(hshift - cshift);
563: }
564: return (r);
1.1 etheisen 565: }
566:
1.5 millert 567: #define IS_UTF8_4BYTE(c) ( ((c) & 0xf8) == 0xf0 )
568: #define IS_UTF8_3BYTE(c) ( ((c) & 0xf0) == 0xe0 )
569: #define IS_UTF8_2BYTE(c) ( ((c) & 0xe0) == 0xc0 )
570: #define IS_UTF8_TRAIL(c) ( ((c) & 0xc0) == 0x80 )
571:
1.1 etheisen 572: static int
573: do_append(c, pos)
574: int c;
575: POSITION pos;
576: {
1.5 millert 577: register char *s;
578: register int a;
1.1 etheisen 579:
1.5 millert 580: #define STOREC(c,a) \
581: if ((c) == '\t') STORE_TAB((a),pos); else STORE_CHAR((c),(a),pos)
1.1 etheisen 582:
1.5 millert 583: if (c == '\b')
584: {
585: switch (bs_mode)
586: {
587: case BS_NORMAL:
588: STORE_CHAR(c, AT_NORMAL, pos);
589: break;
590: case BS_CONTROL:
591: goto do_control_char;
592: case BS_SPECIAL:
593: if (curr == 0)
594: break;
595: backc();
596: overstrike = 1;
597: break;
598: }
599: } else if (overstrike)
1.1 etheisen 600: {
601: /*
602: * Overstrike the character at the current position
603: * in the line buffer. This will cause either
604: * underline (if a "_" is overstruck),
605: * bold (if an identical character is overstruck),
606: * or just deletion of the character in the buffer.
607: */
1.5 millert 608: overstrike--;
609: if (utf_mode && IS_UTF8_4BYTE(c) && curr > 2 && (char)c == linebuf[curr-3])
610: {
611: backc();
612: backc();
613: backc();
614: STORE_CHAR(linebuf[curr], AT_BOLD, pos);
615: overstrike = 3;
616: } else if (utf_mode && (IS_UTF8_3BYTE(c) || (overstrike==2 && IS_UTF8_TRAIL(c))) && curr > 1 && (char)c == linebuf[curr-2])
617: {
618: backc();
619: backc();
620: STORE_CHAR(linebuf[curr], AT_BOLD, pos);
621: overstrike = 2;
622: } else if (utf_mode && curr > 0 && (IS_UTF8_2BYTE(c) || (overstrike==1 && IS_UTF8_TRAIL(c))) && (char)c == linebuf[curr-1])
623: {
624: backc();
625: STORE_CHAR(linebuf[curr], AT_BOLD, pos);
626: overstrike = 1;
627: } else if (utf_mode && curr > 0 && IS_UTF8_TRAIL(c) && attr[curr-1] == AT_UNDERLINE)
628: {
629: STOREC(c, AT_UNDERLINE);
630: } else if ((char)c == linebuf[curr])
631: {
632: /*
633: * Overstriking a char with itself means make it bold.
634: * But overstriking an underscore with itself is
635: * ambiguous. It could mean make it bold, or
636: * it could mean make it underlined.
637: * Use the previous overstrike to resolve it.
638: */
639: if (c == '_' && last_overstrike != AT_NORMAL)
640: STOREC(c, last_overstrike);
641: else
642: STOREC(c, AT_BOLD);
643: } else if (c == '_')
644: {
645: if (utf_mode)
646: {
647: int i;
648: for (i = 0; i < 5; i++)
649: {
650: if (curr <= i || !IS_CONT(linebuf[curr-i]))
651: break;
652: attr[curr-i-1] = AT_UNDERLINE;
653: }
654: }
1.1 etheisen 655: STOREC(linebuf[curr], AT_UNDERLINE);
1.5 millert 656: } else if (linebuf[curr] == '_')
657: {
658: if (utf_mode)
659: {
660: if (IS_UTF8_2BYTE(c))
661: overstrike = 1;
662: else if (IS_UTF8_3BYTE(c))
663: overstrike = 2;
664: else if (IS_UTF8_4BYTE(c))
665: overstrike = 3;
666: }
1.1 etheisen 667: STOREC(c, AT_UNDERLINE);
1.5 millert 668: } else if (control_char(c))
1.1 etheisen 669: goto do_control_char;
670: else
671: STOREC(c, AT_NORMAL);
1.5 millert 672: } else if (c == '\t')
1.1 etheisen 673: {
1.5 millert 674: /*
675: * Expand a tab into spaces.
676: */
1.1 etheisen 677: switch (bs_mode)
678: {
679: case BS_CONTROL:
680: goto do_control_char;
1.5 millert 681: case BS_NORMAL:
1.1 etheisen 682: case BS_SPECIAL:
1.5 millert 683: STORE_TAB(AT_NORMAL, pos);
1.1 etheisen 684: break;
685: }
686: } else if (control_char(c))
687: {
688: do_control_char:
1.5 millert 689: if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && c == ESC))
1.1 etheisen 690: {
691: /*
692: * Output as a normal character.
693: */
1.5 millert 694: STORE_CHAR(c, AT_NORMAL, pos);
1.1 etheisen 695: } else
696: {
697: /*
698: * Convert to printable representation.
699: */
700: s = prchar(c);
701: a = binattr;
702:
703: /*
704: * Make sure we can get the entire representation
705: * of the character on this line.
706: */
707: if (column + (int) strlen(s) +
708: attr_swidth(a) + attr_ewidth(a) > sc_width)
709: return (1);
710:
711: for ( ; *s != 0; s++)
1.5 millert 712: STORE_CHAR(*s, a, pos);
1.1 etheisen 713: }
714: } else
715: {
716: STOREC(c, AT_NORMAL);
717: }
718:
719: return (0);
720: }
721:
722: /*
723: * Terminate the line in the line buffer.
724: */
725: public void
726: pdone(endline)
727: int endline;
728: {
729: if (pendc && (pendc != '\r' || !endline))
730: /*
731: * If we had a pending character, put it in the buffer.
732: * But discard a pending CR if we are at end of line
733: * (that is, discard the CR in a CR/LF sequence).
734: */
735: (void) do_append(pendc, pendpos);
736:
737: /*
1.5 millert 738: * Make sure we've shifted the line, if we need to.
739: */
740: if (cshift < hshift)
741: pshift(hshift - cshift);
742:
743: /*
1.1 etheisen 744: * Add a newline if necessary,
745: * and append a '\0' to the end of the line.
746: */
1.5 millert 747: if (column < sc_width || !auto_wrap || ignaw || ctldisp == OPT_ON)
1.1 etheisen 748: {
749: linebuf[curr] = '\n';
750: attr[curr] = AT_NORMAL;
751: curr++;
752: }
753: linebuf[curr] = '\0';
754: attr[curr] = AT_NORMAL;
1.5 millert 755:
756: #if HILITE_SEARCH
757: if (status_col && hilites > 0)
758: {
759: linebuf[0] = '*';
760: attr[0] = AT_STANDOUT;
761: }
762: #endif
763: /*
764: * If we are done with this line, reset the current shift.
765: */
766: if (endline)
767: cshift = 0;
1.1 etheisen 768: }
769:
770: /*
771: * Get a character from the current line.
772: * Return the character as the function return value,
773: * and the character attribute in *ap.
774: */
775: public int
776: gline(i, ap)
1.5 millert 777: register int i;
778: register int *ap;
1.1 etheisen 779: {
780: char *s;
781:
782: if (is_null_line)
783: {
784: /*
785: * If there is no current line, we pretend the line is
786: * either "~" or "", depending on the "twiddle" flag.
787: */
1.5 millert 788: *ap = AT_BOLD;
1.1 etheisen 789: s = (twiddle) ? "~\n" : "\n";
790: return (s[i]);
791: }
792:
793: *ap = attr[i];
794: return (linebuf[i] & 0377);
795: }
796:
797: /*
798: * Indicate that there is no current line.
799: */
800: public void
801: null_line()
802: {
803: is_null_line = 1;
1.5 millert 804: cshift = 0;
1.1 etheisen 805: }
806:
807: /*
808: * Analogous to forw_line(), but deals with "raw lines":
809: * lines which are not split for screen width.
810: * {{ This is supposed to be more efficient than forw_line(). }}
811: */
812: public POSITION
813: forw_raw_line(curr_pos, linep)
814: POSITION curr_pos;
815: char **linep;
816: {
1.5 millert 817: register int n;
818: register int c;
1.1 etheisen 819: POSITION new_pos;
820:
821: if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
822: (c = ch_forw_get()) == EOI)
823: return (NULL_POSITION);
824:
1.5 millert 825: n = 0;
1.1 etheisen 826: for (;;)
827: {
828: if (c == '\n' || c == EOI)
829: {
830: new_pos = ch_tell();
831: break;
832: }
1.5 millert 833: if (n >= size_linebuf-1)
1.1 etheisen 834: {
1.5 millert 835: if (expand_linebuf())
836: {
837: /*
838: * Overflowed the input buffer.
839: * Pretend the line ended here.
840: */
841: new_pos = ch_tell() - 1;
842: break;
843: }
1.1 etheisen 844: }
1.5 millert 845: linebuf[n++] = c;
1.1 etheisen 846: c = ch_forw_get();
847: }
1.5 millert 848: linebuf[n] = '\0';
1.1 etheisen 849: if (linep != NULL)
850: *linep = linebuf;
851: return (new_pos);
852: }
853:
854: /*
855: * Analogous to back_line(), but deals with "raw lines".
856: * {{ This is supposed to be more efficient than back_line(). }}
857: */
858: public POSITION
859: back_raw_line(curr_pos, linep)
860: POSITION curr_pos;
861: char **linep;
862: {
1.5 millert 863: register int n;
864: register int c;
1.1 etheisen 865: POSITION new_pos;
866:
867: if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
868: ch_seek(curr_pos-1))
869: return (NULL_POSITION);
870:
1.5 millert 871: n = size_linebuf;
872: linebuf[--n] = '\0';
1.1 etheisen 873: for (;;)
874: {
875: c = ch_back_get();
876: if (c == '\n')
877: {
878: /*
879: * This is the newline ending the previous line.
880: * We have hit the beginning of the line.
881: */
882: new_pos = ch_tell() + 1;
883: break;
884: }
885: if (c == EOI)
886: {
887: /*
888: * We have hit the beginning of the file.
889: * This must be the first line in the file.
890: * This must, of course, be the beginning of the line.
891: */
892: new_pos = ch_zero();
893: break;
894: }
1.5 millert 895: if (n <= 0)
1.1 etheisen 896: {
1.5 millert 897: int old_size_linebuf = size_linebuf;
898: char *fm;
899: char *to;
900: if (expand_linebuf())
901: {
902: /*
903: * Overflowed the input buffer.
904: * Pretend the line ended here.
905: */
906: new_pos = ch_tell() + 1;
907: break;
908: }
1.1 etheisen 909: /*
1.5 millert 910: * Shift the data to the end of the new linebuf.
1.1 etheisen 911: */
1.5 millert 912: for (fm = linebuf + old_size_linebuf,
913: to = linebuf + size_linebuf;
914: fm >= linebuf; fm--, to--)
915: *to = *fm;
916: n = size_linebuf - old_size_linebuf;
1.1 etheisen 917: }
1.5 millert 918: linebuf[--n] = c;
1.1 etheisen 919: }
920: if (linep != NULL)
1.5 millert 921: *linep = &linebuf[n];
1.1 etheisen 922: return (new_pos);
923: }