xref: /freebsd/contrib/less/input.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
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 }
407