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