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