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