xref: /freebsd/contrib/less/input.c (revision b601c69bdbe8755d26570261d7fd4c02ee4eff74)
1 /*
2  * Copyright (C) 1984-2000  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 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
45 forw_line(curr_pos)
46 	POSITION curr_pos;
47 {
48 	POSITION new_pos;
49 	register int c;
50 	int blankline;
51 	int endline;
52 
53 	if (curr_pos == NULL_POSITION)
54 	{
55 		null_line();
56 		return (NULL_POSITION);
57 	}
58 #if HILITE_SEARCH
59 	if (hilite_search == OPT_ONPLUS)
60 		/*
61 		 * If we are ignoring EOI (command F), only prepare
62 		 * one line ahead, to avoid getting stuck waiting for
63 		 * slow data without displaying the data we already have.
64 		 * If we're not ignoring EOI, we *could* do the same, but
65 		 * for efficiency we prepare several lines ahead at once.
66 		 */
67 		prep_hilite(curr_pos, curr_pos + 3*size_linebuf,
68 				ignore_eoi ? 1 : -1);
69 #endif
70 	if (ch_seek(curr_pos))
71 	{
72 		null_line();
73 		return (NULL_POSITION);
74 	}
75 
76 	prewind();
77 	plinenum(curr_pos);
78 	(void) ch_seek(curr_pos);
79 
80 	c = ch_forw_get();
81 	if (c == EOI)
82 	{
83 		null_line();
84 		return (NULL_POSITION);
85 	}
86 	blankline = (c == '\n' || c == '\r');
87 
88 	for (;;)
89 	{
90 		if (ABORT_SIGS())
91 		{
92 			null_line();
93 			return (NULL_POSITION);
94 		}
95 		if (c == '\n' || c == EOI)
96 		{
97 			/*
98 			 * End of the line.
99 			 */
100 			new_pos = ch_tell();
101 			endline = TRUE;
102 			break;
103 		}
104 
105 		/*
106 		 * Append the char to the line and get the next char.
107 		 */
108 		if (pappend(c, ch_tell()-1))
109 		{
110 			/*
111 			 * The char won't fit in the line; the line
112 			 * is too long to print in the screen width.
113 			 * End the line here.
114 			 */
115 			if (chopline || hshift > 0)
116 			{
117 				do
118 				{
119 					c = ch_forw_get();
120 				} while (c != '\n' && c != EOI);
121 				new_pos = ch_tell();
122 				endline = TRUE;
123 				quit_if_one_screen = FALSE;
124 			} else
125 			{
126 				new_pos = ch_tell() - 1;
127 				endline = FALSE;
128 			}
129 			break;
130 		}
131 		c = ch_forw_get();
132 	}
133 	pdone(endline);
134 
135 	if (squeeze && blankline)
136 	{
137 		/*
138 		 * This line is blank.
139 		 * Skip down to the last contiguous blank line
140 		 * and pretend it is the one which we are returning.
141 		 */
142 		while ((c = ch_forw_get()) == '\n' || c == '\r')
143 			if (ABORT_SIGS())
144 			{
145 				null_line();
146 				return (NULL_POSITION);
147 			}
148 		if (c != EOI)
149 			(void) ch_back_get();
150 		new_pos = ch_tell();
151 	}
152 
153 	return (new_pos);
154 }
155 
156 /*
157  * Get the previous line.
158  * A "current" position is passed and a "new" position is returned.
159  * The current position is the position of the first character of
160  * a line.  The new position is the position of the first character
161  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
162  */
163 	public POSITION
164 back_line(curr_pos)
165 	POSITION curr_pos;
166 {
167 	POSITION new_pos, begin_new_pos;
168 	int c;
169 	int endline;
170 
171 	if (curr_pos == NULL_POSITION || curr_pos <= ch_zero())
172 	{
173 		null_line();
174 		return (NULL_POSITION);
175 	}
176 #if HILITE_SEARCH
177 	if (hilite_search == OPT_ONPLUS)
178 		prep_hilite((curr_pos < 3*size_linebuf) ?
179 				0 : curr_pos - 3*size_linebuf, curr_pos, -1);
180 #endif
181 	if (ch_seek(curr_pos-1))
182 	{
183 		null_line();
184 		return (NULL_POSITION);
185 	}
186 
187 	if (squeeze)
188 	{
189 		/*
190 		 * Find out if the "current" line was blank.
191 		 */
192 		(void) ch_forw_get();	/* Skip the newline */
193 		c = ch_forw_get();	/* First char of "current" line */
194 		(void) ch_back_get();	/* Restore our position */
195 		(void) ch_back_get();
196 
197 		if (c == '\n' || c == '\r')
198 		{
199 			/*
200 			 * The "current" line was blank.
201 			 * Skip over any preceding blank lines,
202 			 * since we skipped them in forw_line().
203 			 */
204 			while ((c = ch_back_get()) == '\n' || c == '\r')
205 				if (ABORT_SIGS())
206 				{
207 					null_line();
208 					return (NULL_POSITION);
209 				}
210 			if (c == EOI)
211 			{
212 				null_line();
213 				return (NULL_POSITION);
214 			}
215 			(void) ch_forw_get();
216 		}
217 	}
218 
219 	/*
220 	 * Scan backwards until we hit the beginning of the line.
221 	 */
222 	for (;;)
223 	{
224 		if (ABORT_SIGS())
225 		{
226 			null_line();
227 			return (NULL_POSITION);
228 		}
229 		c = ch_back_get();
230 		if (c == '\n')
231 		{
232 			/*
233 			 * This is the newline ending the previous line.
234 			 * We have hit the beginning of the line.
235 			 */
236 			new_pos = ch_tell() + 1;
237 			break;
238 		}
239 		if (c == EOI)
240 		{
241 			/*
242 			 * We have hit the beginning of the file.
243 			 * This must be the first line in the file.
244 			 * This must, of course, be the beginning of the line.
245 			 */
246 			new_pos = ch_tell();
247 			break;
248 		}
249 	}
250 
251 	/*
252 	 * Now scan forwards from the beginning of this line.
253 	 * We keep discarding "printable lines" (based on screen width)
254 	 * until we reach the curr_pos.
255 	 *
256 	 * {{ This algorithm is pretty inefficient if the lines
257 	 *    are much longer than the screen width,
258 	 *    but I don't know of any better way. }}
259 	 */
260 	if (ch_seek(new_pos))
261 	{
262 		null_line();
263 		return (NULL_POSITION);
264 	}
265 	endline = FALSE;
266     loop:
267 	begin_new_pos = new_pos;
268 	prewind();
269 	plinenum(new_pos);
270 	(void) ch_seek(new_pos);
271 
272 	do
273 	{
274 		c = ch_forw_get();
275 		if (c == EOI || ABORT_SIGS())
276 		{
277 			null_line();
278 			return (NULL_POSITION);
279 		}
280 		new_pos++;
281 		if (c == '\n')
282 		{
283 			endline = TRUE;
284 			break;
285 		}
286 		if (pappend(c, ch_tell()-1))
287 		{
288 			/*
289 			 * Got a full printable line, but we haven't
290 			 * reached our curr_pos yet.  Discard the line
291 			 * and start a new one.
292 			 */
293 			if (chopline || hshift > 0)
294 			{
295 				endline = TRUE;
296 				quit_if_one_screen = FALSE;
297 				break;
298 			}
299 			pdone(0);
300 			(void) ch_back_get();
301 			new_pos--;
302 			goto loop;
303 		}
304 	} while (new_pos < curr_pos);
305 
306 	pdone(endline);
307 
308 	return (begin_new_pos);
309 }
310 
311 /*
312  * Set attnpos.
313  */
314 	public void
315 set_attnpos(pos)
316 	POSITION pos;
317 {
318 	int c;
319 
320 	if (pos != NULL_POSITION)
321 	{
322 		if (ch_seek(pos))
323 			return;
324 		for (;;)
325 		{
326 			c = ch_forw_get();
327 			if (c == EOI)
328 				return;
329 			if (c != '\n' && c != '\r')
330 				break;
331 			pos++;
332 		}
333 	}
334 	start_attnpos = pos;
335 	for (;;)
336 	{
337 		c = ch_forw_get();
338 		pos++;
339 		if (c == EOI || c == '\n' || c == '\r')
340 			break;
341 	}
342 	end_attnpos = pos;
343 }
344