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