Merge branch 'vendor/LESS'
[dragonfly.git] / contrib / less / input.c
1 /*
2  * Copyright (C) 1984-2007  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         if (curr_pos == NULL_POSITION)
57         {
58                 null_line();
59                 return (NULL_POSITION);
60         }
61 #if HILITE_SEARCH
62         if (hilite_search == OPT_ONPLUS || status_col)
63                 /*
64                  * If we are ignoring EOI (command F), only prepare
65                  * one line ahead, to avoid getting stuck waiting for
66                  * slow data without displaying the data we already have.
67                  * If we're not ignoring EOI, we *could* do the same, but
68                  * for efficiency we prepare several lines ahead at once.
69                  */
70                 prep_hilite(curr_pos, curr_pos + 3*size_linebuf, 
71                                 ignore_eoi ? 1 : -1);
72 #endif
73         if (ch_seek(curr_pos))
74         {
75                 null_line();
76                 return (NULL_POSITION);
77         }
78
79         base_pos = curr_pos;
80         for (;;)
81         {
82                 if (ABORT_SIGS())
83                 {
84                         null_line();
85                         return (NULL_POSITION);
86                 }
87                 c = ch_back_get();
88                 if (c == EOI)
89                         break;
90                 if (c == '\n')
91                 {
92                         (void) ch_forw_get();
93                         break;
94                 }
95                 --base_pos;
96         }
97
98         prewind();
99         plinenum(base_pos);
100         (void) ch_seek(base_pos);
101         while (base_pos < curr_pos)
102         {
103                 if (ABORT_SIGS())
104                 {
105                         null_line();
106                         return (NULL_POSITION);
107                 }
108                 c = ch_forw_get();
109                 backchars = pappend(c, base_pos);
110                 base_pos++;
111                 if (backchars > 0)
112                 {
113                         pshift_all();
114                         base_pos -= backchars;
115                         while (--backchars >= 0)
116                                 (void) ch_back_get();
117                 }
118         }
119         (void) pflushmbc();
120         pshift_all();
121
122         c = ch_forw_get();
123         if (c == EOI)
124         {
125                 null_line();
126                 return (NULL_POSITION);
127         }
128         blankline = (c == '\n' || c == '\r');
129
130         for (;;)
131         {
132                 if (ABORT_SIGS())
133                 {
134                         null_line();
135                         return (NULL_POSITION);
136                 }
137                 if (c == '\n' || c == EOI)
138                 {
139                         /*
140                          * End of the line.
141                          */
142                         backchars = pflushmbc();
143                         new_pos = ch_tell();
144                         if (backchars > 0 && !chopline && hshift == 0)
145                         {
146                                 new_pos -= backchars + 1;
147                                 endline = FALSE;
148                         } else
149                                 endline = TRUE;
150                         break;
151                 }
152                 if (c != '\r')
153                         blankline = 0;
154
155                 /*
156                  * Append the char to the line and get the next char.
157                  */
158                 backchars = pappend(c, ch_tell()-1);
159                 if (backchars > 0)
160                 {
161                         /*
162                          * The char won't fit in the line; the line
163                          * is too long to print in the screen width.
164                          * End the line here.
165                          */
166                         if (chopline || hshift > 0)
167                         {
168                                 do
169                                 {
170                                         c = ch_forw_get();
171                                 } while (c != '\n' && c != EOI);
172                                 new_pos = ch_tell();
173                                 endline = TRUE;
174                                 quit_if_one_screen = FALSE;
175                         } else
176                         {
177                                 new_pos = ch_tell() - backchars;
178                                 endline = FALSE;
179                         }
180                         break;
181                 }
182                 c = ch_forw_get();
183         }
184         pdone(endline);
185
186         if (squeeze && blankline)
187         {
188                 /*
189                  * This line is blank.
190                  * Skip down to the last contiguous blank line
191                  * and pretend it is the one which we are returning.
192                  */
193                 while ((c = ch_forw_get()) == '\n' || c == '\r')
194                         if (ABORT_SIGS())
195                         {
196                                 null_line();
197                                 return (NULL_POSITION);
198                         }
199                 if (c != EOI)
200                         (void) ch_back_get();
201                 new_pos = ch_tell();
202         }
203
204         return (new_pos);
205 }
206
207 /*
208  * Get the previous line.
209  * A "current" position is passed and a "new" position is returned.
210  * The current position is the position of the first character of
211  * a line.  The new position is the position of the first character
212  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
213  */
214         public POSITION
215 back_line(curr_pos)
216         POSITION curr_pos;
217 {
218         POSITION new_pos, begin_new_pos;
219         int c;
220         int endline;
221         int backchars;
222
223         if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
224         {
225                 null_line();
226                 return (NULL_POSITION);
227         }
228 #if HILITE_SEARCH
229         if (hilite_search == OPT_ONPLUS || status_col)
230                 prep_hilite((curr_pos < 3*size_linebuf) ? 
231                                 0 : curr_pos - 3*size_linebuf, curr_pos, -1);
232 #endif
233         if (ch_seek(curr_pos-1))
234         {
235                 null_line();
236                 return (NULL_POSITION);
237         }
238
239         if (squeeze)
240         {
241                 /*
242                  * Find out if the "current" line was blank.
243                  */
244                 (void) ch_forw_get();   /* Skip the newline */
245                 c = ch_forw_get();      /* First char of "current" line */
246                 (void) ch_back_get();   /* Restore our position */
247                 (void) ch_back_get();
248
249                 if (c == '\n' || c == '\r')
250                 {
251                         /*
252                          * The "current" line was blank.
253                          * Skip over any preceding blank lines,
254                          * since we skipped them in forw_line().
255                          */
256                         while ((c = ch_back_get()) == '\n' || c == '\r')
257                                 if (ABORT_SIGS())
258                                 {
259                                         null_line();
260                                         return (NULL_POSITION);
261                                 }
262                         if (c == EOI)
263                         {
264                                 null_line();
265                                 return (NULL_POSITION);
266                         }
267                         (void) ch_forw_get();
268                 }
269         }
270
271         /*
272          * Scan backwards until we hit the beginning of the line.
273          */
274         for (;;)
275         {
276                 if (ABORT_SIGS())
277                 {
278                         null_line();
279                         return (NULL_POSITION);
280                 }
281                 c = ch_back_get();
282                 if (c == '\n')
283                 {
284                         /*
285                          * This is the newline ending the previous line.
286                          * We have hit the beginning of the line.
287                          */
288                         new_pos = ch_tell() + 1;
289                         break;
290                 }
291                 if (c == EOI)
292                 {
293                         /*
294                          * We have hit the beginning of the file.
295                          * This must be the first line in the file.
296                          * This must, of course, be the beginning of the line.
297                          */
298                         new_pos = ch_tell();
299                         break;
300                 }
301         }
302
303         /*
304          * Now scan forwards from the beginning of this line.
305          * We keep discarding "printable lines" (based on screen width)
306          * until we reach the curr_pos.
307          *
308          * {{ This algorithm is pretty inefficient if the lines
309          *    are much longer than the screen width, 
310          *    but I don't know of any better way. }}
311          */
312         if (ch_seek(new_pos))
313         {
314                 null_line();
315                 return (NULL_POSITION);
316         }
317         endline = FALSE;
318         prewind();
319         plinenum(new_pos);
320     loop:
321         begin_new_pos = new_pos;
322         (void) ch_seek(new_pos);
323
324         do
325         {
326                 c = ch_forw_get();
327                 if (c == EOI || ABORT_SIGS())
328                 {
329                         null_line();
330                         return (NULL_POSITION);
331                 }
332                 new_pos++;
333                 if (c == '\n')
334                 {
335                         backchars = pflushmbc();
336                         if (backchars > 0 && !chopline && hshift == 0)
337                         {
338                                 backchars++;
339                                 goto shift;
340                         }
341                         endline = TRUE;
342                         break;
343                 }
344                 backchars = pappend(c, ch_tell()-1);
345                 if (backchars > 0)
346                 {
347                         /*
348                          * Got a full printable line, but we haven't
349                          * reached our curr_pos yet.  Discard the line
350                          * and start a new one.
351                          */
352                         if (chopline || hshift > 0)
353                         {
354                                 endline = TRUE;
355                                 quit_if_one_screen = FALSE;
356                                 break;
357                         }
358                 shift:
359                         pshift_all();
360                         while (backchars-- > 0)
361                         {
362                                 (void) ch_back_get();
363                                 new_pos--;
364                         }
365                         goto loop;
366                 }
367         } while (new_pos < curr_pos);
368
369         pdone(endline);
370
371         return (begin_new_pos);
372 }
373
374 /*
375  * Set attnpos.
376  */
377         public void
378 set_attnpos(pos)
379         POSITION pos;
380 {
381         int c;
382
383         if (pos != NULL_POSITION)
384         {
385                 if (ch_seek(pos))
386                         return;
387                 for (;;)
388                 {
389                         c = ch_forw_get();
390                         if (c == EOI)
391                                 return;
392                         if (c != '\n' && c != '\r')
393                                 break;
394                         pos++;
395                 }
396         }
397         start_attnpos = pos;
398         for (;;)
399         {
400                 c = ch_forw_get();
401                 pos++;
402                 if (c == EOI || c == '\n' || c == '\r')
403                         break;
404         }
405         end_attnpos = pos;
406 }