]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/less/input.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / less / input.c
1 /*
2  * Copyright (C) 1984-2017  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, see the README file.
8  */
9
10 /*
11  * High level routines dealing with getting lines of input 
12  * from the file being viewed.
13  *
14  * When we speak of "lines" here, we mean PRINTABLE lines;
15  * lines processed with respect to the screen width.
16  * We use the term "raw line" to refer to lines simply
17  * delimited by newlines; not processed with respect to screen width.
18  */
19
20 #include "less.h"
21
22 extern int squeeze;
23 extern int chopline;
24 extern int hshift;
25 extern int quit_if_one_screen;
26 extern int sigs;
27 extern int ignore_eoi;
28 extern int status_col;
29 extern POSITION start_attnpos;
30 extern POSITION end_attnpos;
31 #if HILITE_SEARCH
32 extern int hilite_search;
33 extern int size_linebuf;
34 #endif
35
36 /*
37  * Get the next line.
38  * A "current" position is passed and a "new" position is returned.
39  * The current position is the position of the first character of
40  * a line.  The new position is the position of the first character
41  * of the NEXT line.  The line obtained is the line starting at curr_pos.
42  */
43         public POSITION
44 forw_line(curr_pos)
45         POSITION curr_pos;
46 {
47         POSITION base_pos;
48         POSITION new_pos;
49         int c;
50         int blankline;
51         int endline;
52         int chopped;
53         int backchars;
54
55 get_forw_line:
56         if (curr_pos == NULL_POSITION)
57         {
58                 null_line();
59                 return (NULL_POSITION);
60         }
61 #if HILITE_SEARCH
62         if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
63         {
64                 /*
65                  * If we are ignoring EOI (command F), only prepare
66                  * one line ahead, to avoid getting stuck waiting for
67                  * slow data without displaying the data we already have.
68                  * If we're not ignoring EOI, we *could* do the same, but
69                  * for efficiency we prepare several lines ahead at once.
70                  */
71                 prep_hilite(curr_pos, curr_pos + 3*size_linebuf, 
72                                 ignore_eoi ? 1 : -1);
73                 curr_pos = next_unfiltered(curr_pos);
74         }
75 #endif
76         if (ch_seek(curr_pos))
77         {
78                 null_line();
79                 return (NULL_POSITION);
80         }
81
82         /*
83          * Step back to the beginning of the line.
84          */
85         base_pos = curr_pos;
86         for (;;)
87         {
88                 if (ABORT_SIGS())
89                 {
90                         null_line();
91                         return (NULL_POSITION);
92                 }
93                 c = ch_back_get();
94                 if (c == EOI)
95                         break;
96                 if (c == '\n')
97                 {
98                         (void) ch_forw_get();
99                         break;
100                 }
101                 --base_pos;
102         }
103
104         /*
105          * Read forward again to the position we should start at.
106          */
107         prewind();
108         plinenum(base_pos);
109         (void) ch_seek(base_pos);
110         new_pos = base_pos;
111         while (new_pos < curr_pos)
112         {
113                 if (ABORT_SIGS())
114                 {
115                         null_line();
116                         return (NULL_POSITION);
117                 }
118                 c = ch_forw_get();
119                 backchars = pappend(c, new_pos);
120                 new_pos++;
121                 if (backchars > 0)
122                 {
123                         pshift_all();
124                         new_pos -= backchars;
125                         while (--backchars >= 0)
126                                 (void) ch_back_get();
127                 }
128         }
129         (void) pflushmbc();
130         pshift_all();
131
132         /*
133          * Read the first character to display.
134          */
135         c = ch_forw_get();
136         if (c == EOI)
137         {
138                 null_line();
139                 return (NULL_POSITION);
140         }
141         blankline = (c == '\n' || c == '\r');
142
143         /*
144          * Read each character in the line and append to the line buffer.
145          */
146         chopped = FALSE;
147         for (;;)
148         {
149                 if (ABORT_SIGS())
150                 {
151                         null_line();
152                         return (NULL_POSITION);
153                 }
154                 if (c == '\n' || c == EOI)
155                 {
156                         /*
157                          * End of the line.
158                          */
159                         backchars = pflushmbc();
160                         new_pos = ch_tell();
161                         if (backchars > 0 && !chopline && hshift == 0)
162                         {
163                                 new_pos -= backchars + 1;
164                                 endline = FALSE;
165                         } else
166                                 endline = TRUE;
167                         break;
168                 }
169                 if (c != '\r')
170                         blankline = 0;
171
172                 /*
173                  * Append the char to the line and get the next char.
174                  */
175                 backchars = pappend(c, ch_tell()-1);
176                 if (backchars > 0)
177                 {
178                         /*
179                          * The char won't fit in the line; the line
180                          * is too long to print in the screen width.
181                          * End the line here.
182                          */
183                         if (chopline || hshift > 0)
184                         {
185                                 do
186                                 {
187                                         if (ABORT_SIGS())
188                                         {
189                                                 null_line();
190                                                 return (NULL_POSITION);
191                                         }
192                                         c = ch_forw_get();
193                                 } while (c != '\n' && c != EOI);
194                                 new_pos = ch_tell();
195                                 endline = TRUE;
196                                 quit_if_one_screen = FALSE;
197                                 chopped = TRUE;
198                         } else
199                         {
200                                 new_pos = ch_tell() - backchars;
201                                 endline = FALSE;
202                         }
203                         break;
204                 }
205                 c = ch_forw_get();
206         }
207
208         pdone(endline, chopped, 1);
209
210 #if HILITE_SEARCH
211         if (is_filtered(base_pos))
212         {
213                 /*
214                  * We don't want to display this line.
215                  * Get the next line.
216                  */
217                 curr_pos = new_pos;
218                 goto get_forw_line;
219         }
220
221         if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
222                 set_status_col('*');
223 #endif
224
225         if (squeeze && blankline)
226         {
227                 /*
228                  * This line is blank.
229                  * Skip down to the last contiguous blank line
230                  * and pretend it is the one which we are returning.
231                  */
232                 while ((c = ch_forw_get()) == '\n' || c == '\r')
233                         if (ABORT_SIGS())
234                         {
235                                 null_line();
236                                 return (NULL_POSITION);
237                         }
238                 if (c != EOI)
239                         (void) ch_back_get();
240                 new_pos = ch_tell();
241         }
242
243         return (new_pos);
244 }
245
246 /*
247  * Get the previous line.
248  * A "current" position is passed and a "new" position is returned.
249  * The current position is the position of the first character of
250  * a line.  The new position is the position of the first character
251  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
252  */
253         public POSITION
254 back_line(curr_pos)
255         POSITION curr_pos;
256 {
257         POSITION new_pos, begin_new_pos, base_pos;
258         int c;
259         int endline;
260         int chopped;
261         int backchars;
262
263 get_back_line:
264         if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
265         {
266                 null_line();
267                 return (NULL_POSITION);
268         }
269 #if HILITE_SEARCH
270         if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
271                 prep_hilite((curr_pos < 3*size_linebuf) ? 
272                                 0 : curr_pos - 3*size_linebuf, curr_pos, -1);
273 #endif
274         if (ch_seek(curr_pos-1))
275         {
276                 null_line();
277                 return (NULL_POSITION);
278         }
279
280         if (squeeze)
281         {
282                 /*
283                  * Find out if the "current" line was blank.
284                  */
285                 (void) ch_forw_get();    /* Skip the newline */
286                 c = ch_forw_get();       /* First char of "current" line */
287                 (void) ch_back_get();    /* Restore our position */
288                 (void) ch_back_get();
289
290                 if (c == '\n' || c == '\r')
291                 {
292                         /*
293                          * The "current" line was blank.
294                          * Skip over any preceding blank lines,
295                          * since we skipped them in forw_line().
296                          */
297                         while ((c = ch_back_get()) == '\n' || c == '\r')
298                                 if (ABORT_SIGS())
299                                 {
300                                         null_line();
301                                         return (NULL_POSITION);
302                                 }
303                         if (c == EOI)
304                         {
305                                 null_line();
306                                 return (NULL_POSITION);
307                         }
308                         (void) ch_forw_get();
309                 }
310         }
311
312         /*
313          * Scan backwards until we hit the beginning of the line.
314          */
315         for (;;)
316         {
317                 if (ABORT_SIGS())
318                 {
319                         null_line();
320                         return (NULL_POSITION);
321                 }
322                 c = ch_back_get();
323                 if (c == '\n')
324                 {
325                         /*
326                          * This is the newline ending the previous line.
327                          * We have hit the beginning of the line.
328                          */
329                         base_pos = ch_tell() + 1;
330                         break;
331                 }
332                 if (c == EOI)
333                 {
334                         /*
335                          * We have hit the beginning of the file.
336                          * This must be the first line in the file.
337                          * This must, of course, be the beginning of the line.
338                          */
339                         base_pos = ch_tell();
340                         break;
341                 }
342         }
343
344         /*
345          * Now scan forwards from the beginning of this line.
346          * We keep discarding "printable lines" (based on screen width)
347          * until we reach the curr_pos.
348          *
349          * {{ This algorithm is pretty inefficient if the lines
350          *    are much longer than the screen width, 
351          *    but I don't know of any better way. }}
352          */
353         new_pos = base_pos;
354         if (ch_seek(new_pos))
355         {
356                 null_line();
357                 return (NULL_POSITION);
358         }
359         endline = FALSE;
360         prewind();
361         plinenum(new_pos);
362     loop:
363         begin_new_pos = new_pos;
364         (void) ch_seek(new_pos);
365         chopped = FALSE;
366
367         do
368         {
369                 c = ch_forw_get();
370                 if (c == EOI || ABORT_SIGS())
371                 {
372                         null_line();
373                         return (NULL_POSITION);
374                 }
375                 new_pos++;
376                 if (c == '\n')
377                 {
378                         backchars = pflushmbc();
379                         if (backchars > 0 && !chopline && hshift == 0)
380                         {
381                                 backchars++;
382                                 goto shift;
383                         }
384                         endline = TRUE;
385                         break;
386                 }
387                 backchars = pappend(c, ch_tell()-1);
388                 if (backchars > 0)
389                 {
390                         /*
391                          * Got a full printable line, but we haven't
392                          * reached our curr_pos yet.  Discard the line
393                          * and start a new one.
394                          */
395                         if (chopline || hshift > 0)
396                         {
397                                 endline = TRUE;
398                                 chopped = TRUE;
399                                 quit_if_one_screen = FALSE;
400                                 break;
401                         }
402                 shift:
403                         pshift_all();
404                         while (backchars-- > 0)
405                         {
406                                 (void) ch_back_get();
407                                 new_pos--;
408                         }
409                         goto loop;
410                 }
411         } while (new_pos < curr_pos);
412
413         pdone(endline, chopped, 0);
414
415 #if HILITE_SEARCH
416         if (is_filtered(base_pos))
417         {
418                 /*
419                  * We don't want to display this line.
420                  * Get the previous line.
421                  */
422                 curr_pos = begin_new_pos;
423                 goto get_back_line;
424         }
425
426         if (status_col && curr_pos > 0 && is_hilited(base_pos, curr_pos-1, 1, NULL))
427                 set_status_col('*');
428 #endif
429
430         return (begin_new_pos);
431 }
432
433 /*
434  * Set attnpos.
435  */
436         public void
437 set_attnpos(pos)
438         POSITION pos;
439 {
440         int c;
441
442         if (pos != NULL_POSITION)
443         {
444                 if (ch_seek(pos))
445                         return;
446                 for (;;)
447                 {
448                         c = ch_forw_get();
449                         if (c == EOI)
450                                 break;
451                         if (c == '\n' || c == '\r')
452                         {
453                                 (void) ch_back_get();
454                                 break;
455                         }
456                         pos++;
457                 }
458                 end_attnpos = pos;
459                 for (;;)
460                 {
461                         c = ch_back_get();
462                         if (c == EOI || c == '\n' || c == '\r')
463                                 break;
464                         pos--;
465                 }
466         }
467         start_attnpos = pos;
468 }