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