1/*	$NetBSD: input.c,v 1.5 2003/08/07 09:27:59 agc Exp $	*/
2
3/*
4 * Copyright (c) 1988 Mark Nudelman
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34#ifndef lint
35#if 0
36static char sccsid[] = "@(#)input.c	8.1 (Berkeley) 6/6/93";
37#else
38__RCSID("$NetBSD: input.c,v 1.5 2003/08/07 09:27:59 agc Exp $");
39#endif
40#endif /* not lint */
41
42/*
43 * High level routines dealing with getting lines of input
44 * from the file being viewed.
45 *
46 * When we speak of "lines" here, we mean PRINTABLE lines;
47 * lines processed with respect to the screen width.
48 * We use the term "raw line" to refer to lines simply
49 * delimited by newlines; not processed with respect to screen width.
50 */
51
52#include <sys/types.h>
53
54#include "less.h"
55#include "extern.h"
56
57/*
58 * Get the next line.
59 * A "current" position is passed and a "new" position is returned.
60 * The current position is the position of the first character of
61 * a line.  The new position is the position of the first character
62 * of the NEXT line.  The line obtained is the line starting at curr_pos.
63 */
64off_t
65forw_line(curr_pos)
66	off_t curr_pos;
67{
68	off_t new_pos;
69	int c;
70
71	if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
72		return (NULL_POSITION);
73
74	c = ch_forw_get();
75	if (c == EOI)
76		return (NULL_POSITION);
77
78	prewind();
79	for (;;)
80	{
81		if (sigs)
82			return (NULL_POSITION);
83		if (c == '\n' || c == EOI)
84		{
85			/*
86			 * End of the line.
87			 */
88			new_pos = ch_tell();
89			break;
90		}
91
92		/*
93		 * Append the char to the line and get the next char.
94		 */
95		if (pappend(c))
96		{
97			/*
98			 * The char won't fit in the line; the line
99			 * is too long to print in the screen width.
100			 * End the line here.
101			 */
102			new_pos = ch_tell() - 1;
103			break;
104		}
105		c = ch_forw_get();
106	}
107	(void) pappend('\0');
108
109	if (squeeze && *line == '\0')
110	{
111		/*
112		 * This line is blank.
113		 * Skip down to the last contiguous blank line
114		 * and pretend it is the one which we are returning.
115		 */
116		while ((c = ch_forw_get()) == '\n')
117			if (sigs)
118				return (NULL_POSITION);
119		if (c != EOI)
120			(void) ch_back_get();
121		new_pos = ch_tell();
122	}
123
124	return (new_pos);
125}
126
127/*
128 * Get the previous line.
129 * A "current" position is passed and a "new" position is returned.
130 * The current position is the position of the first character of
131 * a line.  The new position is the position of the first character
132 * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
133 */
134off_t
135back_line(curr_pos)
136	off_t curr_pos;
137{
138	off_t new_pos, begin_new_pos;
139	int c;
140
141	if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 ||
142		ch_seek(curr_pos-1))
143		return (NULL_POSITION);
144
145	if (squeeze)
146	{
147		/*
148		 * Find out if the "current" line was blank.
149		 */
150		(void) ch_forw_get();	/* Skip the newline */
151		c = ch_forw_get();	/* First char of "current" line */
152		(void) ch_back_get();	/* Restore our position */
153		(void) ch_back_get();
154
155		if (c == '\n')
156		{
157			/*
158			 * The "current" line was blank.
159			 * Skip over any preceding blank lines,
160			 * since we skipped them in forw_line().
161			 */
162			while ((c = ch_back_get()) == '\n')
163				if (sigs)
164					return (NULL_POSITION);
165			if (c == EOI)
166				return (NULL_POSITION);
167			(void) ch_forw_get();
168		}
169	}
170
171	/*
172	 * Scan backwards until we hit the beginning of the line.
173	 */
174	for (;;)
175	{
176		if (sigs)
177			return (NULL_POSITION);
178		c = ch_back_get();
179		if (c == '\n')
180		{
181			/*
182			 * This is the newline ending the previous line.
183			 * We have hit the beginning of the line.
184			 */
185			new_pos = ch_tell() + 1;
186			break;
187		}
188		if (c == EOI)
189		{
190			/*
191			 * We have hit the beginning of the file.
192			 * This must be the first line in the file.
193			 * This must, of course, be the beginning of the line.
194			 */
195			new_pos = ch_tell();
196			break;
197		}
198	}
199
200	/*
201	 * Now scan forwards from the beginning of this line.
202	 * We keep discarding "printable lines" (based on screen width)
203	 * until we reach the curr_pos.
204	 *
205	 * {{ This algorithm is pretty inefficient if the lines
206	 *    are much longer than the screen width,
207	 *    but I don't know of any better way. }}
208	 */
209	if (ch_seek(new_pos))
210		return (NULL_POSITION);
211    loop:
212	begin_new_pos = new_pos;
213	prewind();
214
215	do
216	{
217		c = ch_forw_get();
218		if (c == EOI || sigs)
219			return (NULL_POSITION);
220		new_pos++;
221		if (c == '\n')
222			break;
223		if (pappend(c))
224		{
225			/*
226			 * Got a full printable line, but we haven't
227			 * reached our curr_pos yet.  Discard the line
228			 * and start a new one.
229			 */
230			(void) pappend('\0');
231			(void) ch_back_get();
232			new_pos--;
233			goto loop;
234		}
235	} while (new_pos < curr_pos);
236
237	(void) pappend('\0');
238
239	return (begin_new_pos);
240}
241