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