Import less-443.
[dragonfly.git] / contrib / less / input.c
1 /*
2  * Copyright (C) 1984-2011  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                                         if (ABORT_SIGS())
185                                         {
186                                                 null_line();
187                                                 return (NULL_POSITION);
188                                         }
189                                         c = ch_forw_get();
190                                 } while (c != '\n' && c != EOI);
191                                 new_pos = ch_tell();
192                                 endline = TRUE;
193                                 quit_if_one_screen = FALSE;
194                         } else
195                         {
196                                 new_pos = ch_tell() - backchars;
197                                 endline = FALSE;
198                         }
199                         break;
200                 }
201                 c = ch_forw_get();
202         }
203
204         pdone(endline, 1);
205
206 #if HILITE_SEARCH
207         if (is_filtered(base_pos))
208         {
209                 /*
210                  * We don't want to display this line.
211                  * Get the next line.
212                  */
213                 curr_pos = new_pos;
214                 goto get_forw_line;
215         }
216
217         if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
218                 set_status_col('*');
219 #endif
220
221         if (squeeze && blankline)
222         {
223                 /*
224                  * This line is blank.
225                  * Skip down to the last contiguous blank line
226                  * and pretend it is the one which we are returning.
227                  */
228                 while ((c = ch_forw_get()) == '\n' || c == '\r')
229                         if (ABORT_SIGS())
230                         {
231                                 null_line();
232                                 return (NULL_POSITION);
233                         }
234                 if (c != EOI)
235                         (void) ch_back_get();
236                 new_pos = ch_tell();
237         }
238
239         return (new_pos);
240 }
241
242 /*
243  * Get the previous line.
244  * A "current" position is passed and a "new" position is returned.
245  * The current position is the position of the first character of
246  * a line.  The new position is the position of the first character
247  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
248  */
249         public POSITION
250 back_line(curr_pos)
251         POSITION curr_pos;
252 {
253         POSITION new_pos, begin_new_pos, base_pos;
254         int c;
255         int endline;
256         int backchars;
257
258 get_back_line:
259         if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
260         {
261                 null_line();
262                 return (NULL_POSITION);
263         }
264 #if HILITE_SEARCH
265         if (hilite_search == OPT_ONPLUS || is_filtering() || status_col)
266                 prep_hilite((curr_pos < 3*size_linebuf) ? 
267                                 0 : curr_pos - 3*size_linebuf, curr_pos, -1);
268 #endif
269         if (ch_seek(curr_pos-1))
270         {
271                 null_line();
272                 return (NULL_POSITION);
273         }
274
275         if (squeeze)
276         {
277                 /*
278                  * Find out if the "current" line was blank.
279                  */
280                 (void) ch_forw_get();    /* Skip the newline */
281                 c = ch_forw_get();       /* First char of "current" line */
282                 (void) ch_back_get();    /* Restore our position */
283                 (void) ch_back_get();
284
285                 if (c == '\n' || c == '\r')
286                 {
287                         /*
288                          * The "current" line was blank.
289                          * Skip over any preceding blank lines,
290                          * since we skipped them in forw_line().
291                          */
292                         while ((c = ch_back_get()) == '\n' || c == '\r')
293                                 if (ABORT_SIGS())
294                                 {
295                                         null_line();
296                                         return (NULL_POSITION);
297                                 }
298                         if (c == EOI)
299                         {
300                                 null_line();
301                                 return (NULL_POSITION);
302                         }
303                         (void) ch_forw_get();
304                 }
305         }
306
307         /*
308          * Scan backwards until we hit the beginning of the line.
309          */
310         for (;;)
311         {
312                 if (ABORT_SIGS())
313                 {
314                         null_line();
315                         return (NULL_POSITION);
316                 }
317                 c = ch_back_get();
318                 if (c == '\n')
319                 {
320                         /*
321                          * This is the newline ending the previous line.
322                          * We have hit the beginning of the line.
323                          */
324                         base_pos = ch_tell() + 1;
325                         break;
326                 }
327                 if (c == EOI)
328                 {
329                         /*
330                          * We have hit the beginning of the file.
331                          * This must be the first line in the file.
332                          * This must, of course, be the beginning of the line.
333                          */
334                         base_pos = ch_tell();
335                         break;
336                 }
337         }
338
339         /*
340          * Now scan forwards from the beginning of this line.
341          * We keep discarding "printable lines" (based on screen width)
342          * until we reach the curr_pos.
343          *
344          * {{ This algorithm is pretty inefficient if the lines
345          *    are much longer than the screen width, 
346          *    but I don't know of any better way. }}
347          */
348         new_pos = base_pos;
349         if (ch_seek(new_pos))
350         {
351                 null_line();
352                 return (NULL_POSITION);
353         }
354         endline = FALSE;
355         prewind();
356         plinenum(new_pos);
357     loop:
358         begin_new_pos = new_pos;
359         (void) ch_seek(new_pos);
360
361         do
362         {
363                 c = ch_forw_get();
364                 if (c == EOI || ABORT_SIGS())
365                 {
366                         null_line();
367                         return (NULL_POSITION);
368                 }
369                 new_pos++;
370                 if (c == '\n')
371                 {
372                         backchars = pflushmbc();
373                         if (backchars > 0 && !chopline && hshift == 0)
374                         {
375                                 backchars++;
376                                 goto shift;
377                         }
378                         endline = TRUE;
379                         break;
380                 }
381                 backchars = pappend(c, ch_tell()-1);
382                 if (backchars > 0)
383                 {
384                         /*
385                          * Got a full printable line, but we haven't
386                          * reached our curr_pos yet.  Discard the line
387                          * and start a new one.
388                          */
389                         if (chopline || hshift > 0)
390                         {
391                                 endline = TRUE;
392                                 quit_if_one_screen = FALSE;
393                                 break;
394                         }
395                 shift:
396                         pshift_all();
397                         while (backchars-- > 0)
398                         {
399                                 (void) ch_back_get();
400                                 new_pos--;
401                         }
402                         goto loop;
403                 }
404         } while (new_pos < curr_pos);
405
406         pdone(endline, 0);
407
408 #if HILITE_SEARCH
409         if (is_filtered(base_pos))
410         {
411                 /*
412                  * We don't want to display this line.
413                  * Get the previous line.
414                  */
415                 curr_pos = begin_new_pos;
416                 goto get_back_line;
417         }
418
419         if (status_col && is_hilited(base_pos, ch_tell()-1, 1, NULL))
420                 set_status_col('*');
421 #endif
422
423         return (begin_new_pos);
424 }
425
426 /*
427  * Set attnpos.
428  */
429         public void
430 set_attnpos(pos)
431         POSITION pos;
432 {
433         int c;
434
435         if (pos != NULL_POSITION)
436         {
437                 if (ch_seek(pos))
438                         return;
439                 for (;;)
440                 {
441                         c = ch_forw_get();
442                         if (c == EOI)
443                                 return;
444                         if (c != '\n' && c != '\r')
445                                 break;
446                         pos++;
447                 }
448         }
449         start_attnpos = pos;
450         for (;;)
451         {
452                 c = ch_forw_get();
453                 pos++;
454                 if (c == EOI || c == '\n' || c == '\r')
455                         break;
456         }
457         end_attnpos = pos;
458 }