xref: /netbsd/distrib/utils/more/input.c (revision 6550d01e)
1 /*	$NetBSD: input.c,v 1.6 2003/10/13 14:34:25 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
36 static char sccsid[] = "@(#)input.c	8.1 (Berkeley) 6/6/93";
37 #else
38 __RCSID("$NetBSD: input.c,v 1.6 2003/10/13 14:34:25 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  */
64 off_t
65 forw_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  */
134 off_t
135 back_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