]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/less/line.c
This commit was generated by cvs2svn to compensate for changes in r124361,
[FreeBSD/FreeBSD.git] / contrib / less / line.c
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 }