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