xref: /minix3/external/bsd/less/dist/linenum.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*	$NetBSD: linenum.c,v 1.4 2013/09/04 19:44:21 tron Exp $	*/
2f7cf2976SLionel Sambuc 
3f7cf2976SLionel Sambuc /*
4*84d9c625SLionel Sambuc  * Copyright (C) 1984-2012  Mark Nudelman
5f7cf2976SLionel Sambuc  *
6f7cf2976SLionel Sambuc  * You may distribute under the terms of either the GNU General Public
7f7cf2976SLionel Sambuc  * License or the Less License, as specified in the README file.
8f7cf2976SLionel Sambuc  *
9*84d9c625SLionel Sambuc  * For more information, see the README file.
10f7cf2976SLionel Sambuc  */
11f7cf2976SLionel Sambuc 
12f7cf2976SLionel Sambuc 
13f7cf2976SLionel Sambuc /*
14f7cf2976SLionel Sambuc  * Code to handle displaying line numbers.
15f7cf2976SLionel Sambuc  *
16f7cf2976SLionel Sambuc  * Finding the line number of a given file position is rather tricky.
17f7cf2976SLionel Sambuc  * We don't want to just start at the beginning of the file and
18f7cf2976SLionel Sambuc  * count newlines, because that is slow for large files (and also
19f7cf2976SLionel Sambuc  * wouldn't work if we couldn't get to the start of the file; e.g.
20f7cf2976SLionel Sambuc  * if input is a long pipe).
21f7cf2976SLionel Sambuc  *
22f7cf2976SLionel Sambuc  * So we use the function add_lnum to cache line numbers.
23f7cf2976SLionel Sambuc  * We try to be very clever and keep only the more interesting
24f7cf2976SLionel Sambuc  * line numbers when we run out of space in our table.  A line
25f7cf2976SLionel Sambuc  * number is more interesting than another when it is far from
26f7cf2976SLionel Sambuc  * other line numbers.   For example, we'd rather keep lines
27f7cf2976SLionel Sambuc  * 100,200,300 than 100,101,300.  200 is more interesting than
28f7cf2976SLionel Sambuc  * 101 because 101 can be derived very cheaply from 100, while
29f7cf2976SLionel Sambuc  * 200 is more expensive to derive from 100.
30f7cf2976SLionel Sambuc  *
31f7cf2976SLionel Sambuc  * The function currline() returns the line number of a given
32f7cf2976SLionel Sambuc  * position in the file.  As a side effect, it calls add_lnum
33f7cf2976SLionel Sambuc  * to cache the line number.  Therefore currline is occasionally
34f7cf2976SLionel Sambuc  * called to make sure we cache line numbers often enough.
35f7cf2976SLionel Sambuc  */
36f7cf2976SLionel Sambuc 
37f7cf2976SLionel Sambuc #include "less.h"
38f7cf2976SLionel Sambuc 
39f7cf2976SLionel Sambuc /*
40f7cf2976SLionel Sambuc  * Structure to keep track of a line number and the associated file position.
41f7cf2976SLionel Sambuc  * A doubly-linked circular list of line numbers is kept ordered by line number.
42f7cf2976SLionel Sambuc  */
43f7cf2976SLionel Sambuc struct linenum_info
44f7cf2976SLionel Sambuc {
45f7cf2976SLionel Sambuc 	struct linenum_info *next;	/* Link to next in the list */
46f7cf2976SLionel Sambuc 	struct linenum_info *prev;	/* Line to previous in the list */
47f7cf2976SLionel Sambuc 	POSITION pos;			/* File position */
48f7cf2976SLionel Sambuc 	POSITION gap;			/* Gap between prev and next */
49f7cf2976SLionel Sambuc 	LINENUM line;			/* Line number */
50f7cf2976SLionel Sambuc };
51f7cf2976SLionel Sambuc /*
52f7cf2976SLionel Sambuc  * "gap" needs some explanation: the gap of any particular line number
53f7cf2976SLionel Sambuc  * is the distance between the previous one and the next one in the list.
54f7cf2976SLionel Sambuc  * ("Distance" means difference in file position.)  In other words, the
55f7cf2976SLionel Sambuc  * gap of a line number is the gap which would be introduced if this
56f7cf2976SLionel Sambuc  * line number were deleted.  It is used to decide which one to replace
57f7cf2976SLionel Sambuc  * when we have a new one to insert and the table is full.
58f7cf2976SLionel Sambuc  */
59f7cf2976SLionel Sambuc 
60f7cf2976SLionel Sambuc #define	NPOOL	200			/* Size of line number pool */
61f7cf2976SLionel Sambuc 
62f7cf2976SLionel Sambuc #define	LONGTIME	(2)		/* In seconds */
63f7cf2976SLionel Sambuc 
64f7cf2976SLionel Sambuc static struct linenum_info anchor;	/* Anchor of the list */
65f7cf2976SLionel Sambuc static struct linenum_info *freelist;	/* Anchor of the unused entries */
66f7cf2976SLionel Sambuc static struct linenum_info pool[NPOOL];	/* The pool itself */
67f7cf2976SLionel Sambuc static struct linenum_info *spare;		/* We always keep one spare entry */
68f7cf2976SLionel Sambuc 
69f7cf2976SLionel Sambuc extern int linenums;
70f7cf2976SLionel Sambuc extern int sigs;
71f7cf2976SLionel Sambuc extern int sc_height;
72f7cf2976SLionel Sambuc extern int screen_trashed;
73f7cf2976SLionel Sambuc 
74f7cf2976SLionel Sambuc static void calcgap __P((struct linenum_info *));
75f7cf2976SLionel Sambuc static void longloopmessage __P((void));
76f7cf2976SLionel Sambuc static void longish __P((void));
77f7cf2976SLionel Sambuc 
78f7cf2976SLionel Sambuc /*
79f7cf2976SLionel Sambuc  * Initialize the line number structures.
80f7cf2976SLionel Sambuc  */
81f7cf2976SLionel Sambuc 	public void
clr_linenum()82f7cf2976SLionel Sambuc clr_linenum()
83f7cf2976SLionel Sambuc {
84f7cf2976SLionel Sambuc 	register struct linenum_info *p;
85f7cf2976SLionel Sambuc 
86f7cf2976SLionel Sambuc 	/*
87f7cf2976SLionel Sambuc 	 * Put all the entries on the free list.
88f7cf2976SLionel Sambuc 	 * Leave one for the "spare".
89f7cf2976SLionel Sambuc 	 */
90f7cf2976SLionel Sambuc 	for (p = pool;  p < &pool[NPOOL-2];  p++)
91f7cf2976SLionel Sambuc 		p->next = p+1;
92f7cf2976SLionel Sambuc 	pool[NPOOL-2].next = NULL;
93f7cf2976SLionel Sambuc 	freelist = pool;
94f7cf2976SLionel Sambuc 
95f7cf2976SLionel Sambuc 	spare = &pool[NPOOL-1];
96f7cf2976SLionel Sambuc 
97f7cf2976SLionel Sambuc 	/*
98f7cf2976SLionel Sambuc 	 * Initialize the anchor.
99f7cf2976SLionel Sambuc 	 */
100f7cf2976SLionel Sambuc 	anchor.next = anchor.prev = &anchor;
101f7cf2976SLionel Sambuc 	anchor.gap = 0;
102f7cf2976SLionel Sambuc 	anchor.pos = (POSITION)0;
103f7cf2976SLionel Sambuc 	anchor.line = 1;
104f7cf2976SLionel Sambuc }
105f7cf2976SLionel Sambuc 
106f7cf2976SLionel Sambuc /*
107f7cf2976SLionel Sambuc  * Calculate the gap for an entry.
108f7cf2976SLionel Sambuc  */
109f7cf2976SLionel Sambuc 	static void
calcgap(p)110f7cf2976SLionel Sambuc calcgap(p)
111f7cf2976SLionel Sambuc 	register struct linenum_info *p;
112f7cf2976SLionel Sambuc {
113f7cf2976SLionel Sambuc 	/*
114f7cf2976SLionel Sambuc 	 * Don't bother to compute a gap for the anchor.
115f7cf2976SLionel Sambuc 	 * Also don't compute a gap for the last one in the list.
116f7cf2976SLionel Sambuc 	 * The gap for that last one should be considered infinite,
117f7cf2976SLionel Sambuc 	 * but we never look at it anyway.
118f7cf2976SLionel Sambuc 	 */
119f7cf2976SLionel Sambuc 	if (p == &anchor || p->next == &anchor)
120f7cf2976SLionel Sambuc 		return;
121f7cf2976SLionel Sambuc 	p->gap = p->next->pos - p->prev->pos;
122f7cf2976SLionel Sambuc }
123f7cf2976SLionel Sambuc 
124f7cf2976SLionel Sambuc /*
125f7cf2976SLionel Sambuc  * Add a new line number to the cache.
126f7cf2976SLionel Sambuc  * The specified position (pos) should be the file position of the
127f7cf2976SLionel Sambuc  * FIRST character in the specified line.
128f7cf2976SLionel Sambuc  */
129f7cf2976SLionel Sambuc 	public void
add_lnum(linenum,pos)130f7cf2976SLionel Sambuc add_lnum(linenum, pos)
131f7cf2976SLionel Sambuc 	LINENUM linenum;
132f7cf2976SLionel Sambuc 	POSITION pos;
133f7cf2976SLionel Sambuc {
134f7cf2976SLionel Sambuc 	register struct linenum_info *p;
135f7cf2976SLionel Sambuc 	register struct linenum_info *new;
136f7cf2976SLionel Sambuc 	register struct linenum_info *nextp;
137f7cf2976SLionel Sambuc 	register struct linenum_info *prevp;
138f7cf2976SLionel Sambuc 	register POSITION mingap;
139f7cf2976SLionel Sambuc 
140f7cf2976SLionel Sambuc 	/*
141f7cf2976SLionel Sambuc 	 * Find the proper place in the list for the new one.
142f7cf2976SLionel Sambuc 	 * The entries are sorted by position.
143f7cf2976SLionel Sambuc 	 */
144f7cf2976SLionel Sambuc 	for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
145f7cf2976SLionel Sambuc 		if (p->line == linenum)
146f7cf2976SLionel Sambuc 			/* We already have this one. */
147f7cf2976SLionel Sambuc 			return;
148f7cf2976SLionel Sambuc 	nextp = p;
149f7cf2976SLionel Sambuc 	prevp = p->prev;
150f7cf2976SLionel Sambuc 
151f7cf2976SLionel Sambuc 	if (freelist != NULL)
152f7cf2976SLionel Sambuc 	{
153f7cf2976SLionel Sambuc 		/*
154f7cf2976SLionel Sambuc 		 * We still have free (unused) entries.
155f7cf2976SLionel Sambuc 		 * Use one of them.
156f7cf2976SLionel Sambuc 		 */
157f7cf2976SLionel Sambuc 		new = freelist;
158f7cf2976SLionel Sambuc 		freelist = freelist->next;
159f7cf2976SLionel Sambuc 	} else
160f7cf2976SLionel Sambuc 	{
161f7cf2976SLionel Sambuc 		/*
162f7cf2976SLionel Sambuc 		 * No free entries.
163f7cf2976SLionel Sambuc 		 * Use the "spare" entry.
164f7cf2976SLionel Sambuc 		 */
165f7cf2976SLionel Sambuc 		new = spare;
166f7cf2976SLionel Sambuc 		spare = NULL;
167f7cf2976SLionel Sambuc 	}
168f7cf2976SLionel Sambuc 
169f7cf2976SLionel Sambuc 	/*
170f7cf2976SLionel Sambuc 	 * Fill in the fields of the new entry,
171f7cf2976SLionel Sambuc 	 * and insert it into the proper place in the list.
172f7cf2976SLionel Sambuc 	 */
173f7cf2976SLionel Sambuc 	new->next = nextp;
174f7cf2976SLionel Sambuc 	new->prev = prevp;
175f7cf2976SLionel Sambuc 	new->pos = pos;
176f7cf2976SLionel Sambuc 	new->line = linenum;
177f7cf2976SLionel Sambuc 
178f7cf2976SLionel Sambuc 	nextp->prev = new;
179f7cf2976SLionel Sambuc 	prevp->next = new;
180f7cf2976SLionel Sambuc 
181f7cf2976SLionel Sambuc 	/*
182f7cf2976SLionel Sambuc 	 * Recalculate gaps for the new entry and the neighboring entries.
183f7cf2976SLionel Sambuc 	 */
184f7cf2976SLionel Sambuc 	calcgap(new);
185f7cf2976SLionel Sambuc 	calcgap(nextp);
186f7cf2976SLionel Sambuc 	calcgap(prevp);
187f7cf2976SLionel Sambuc 
188f7cf2976SLionel Sambuc 	if (spare == NULL)
189f7cf2976SLionel Sambuc 	{
190f7cf2976SLionel Sambuc 		/*
191f7cf2976SLionel Sambuc 		 * We have used the spare entry.
192f7cf2976SLionel Sambuc 		 * Scan the list to find the one with the smallest
193f7cf2976SLionel Sambuc 		 * gap, take it out and make it the spare.
194f7cf2976SLionel Sambuc 		 * We should never remove the last one, so stop when
195f7cf2976SLionel Sambuc 		 * we get to p->next == &anchor.  This also avoids
196f7cf2976SLionel Sambuc 		 * looking at the gap of the last one, which is
197f7cf2976SLionel Sambuc 		 * not computed by calcgap.
198f7cf2976SLionel Sambuc 		 */
199f7cf2976SLionel Sambuc 		mingap = anchor.next->gap;
200f7cf2976SLionel Sambuc 		for (p = anchor.next;  p->next != &anchor;  p = p->next)
201f7cf2976SLionel Sambuc 		{
202f7cf2976SLionel Sambuc 			if (p->gap <= mingap)
203f7cf2976SLionel Sambuc 			{
204f7cf2976SLionel Sambuc 				spare = p;
205f7cf2976SLionel Sambuc 				mingap = p->gap;
206f7cf2976SLionel Sambuc 			}
207f7cf2976SLionel Sambuc 		}
208f7cf2976SLionel Sambuc 		spare->next->prev = spare->prev;
209f7cf2976SLionel Sambuc 		spare->prev->next = spare->next;
210f7cf2976SLionel Sambuc 	}
211f7cf2976SLionel Sambuc }
212f7cf2976SLionel Sambuc 
213f7cf2976SLionel Sambuc /*
214f7cf2976SLionel Sambuc  * If we get stuck in a long loop trying to figure out the
215f7cf2976SLionel Sambuc  * line number, print a message to tell the user what we're doing.
216f7cf2976SLionel Sambuc  */
217f7cf2976SLionel Sambuc 	static void
longloopmessage()218f7cf2976SLionel Sambuc longloopmessage()
219f7cf2976SLionel Sambuc {
220f7cf2976SLionel Sambuc 	ierror("Calculating line numbers", NULL_PARG);
221f7cf2976SLionel Sambuc }
222f7cf2976SLionel Sambuc 
223f7cf2976SLionel Sambuc static int loopcount;
224f7cf2976SLionel Sambuc #if HAVE_TIME
225f7cf2976SLionel Sambuc static long startime;
226f7cf2976SLionel Sambuc #endif
227f7cf2976SLionel Sambuc 
228f7cf2976SLionel Sambuc 	static void
longish()229f7cf2976SLionel Sambuc longish()
230f7cf2976SLionel Sambuc {
231f7cf2976SLionel Sambuc #if HAVE_TIME
232f7cf2976SLionel Sambuc 	if (loopcount >= 0 && ++loopcount > 100)
233f7cf2976SLionel Sambuc 	{
234f7cf2976SLionel Sambuc 		loopcount = 0;
235f7cf2976SLionel Sambuc 		if (get_time() >= startime + LONGTIME)
236f7cf2976SLionel Sambuc 		{
237f7cf2976SLionel Sambuc 			longloopmessage();
238f7cf2976SLionel Sambuc 			loopcount = -1;
239f7cf2976SLionel Sambuc 		}
240f7cf2976SLionel Sambuc 	}
241f7cf2976SLionel Sambuc #else
242f7cf2976SLionel Sambuc 	if (loopcount >= 0 && ++loopcount > LONGLOOP)
243f7cf2976SLionel Sambuc 	{
244f7cf2976SLionel Sambuc 		longloopmessage();
245f7cf2976SLionel Sambuc 		loopcount = -1;
246f7cf2976SLionel Sambuc 	}
247f7cf2976SLionel Sambuc #endif
248f7cf2976SLionel Sambuc }
249f7cf2976SLionel Sambuc 
250f7cf2976SLionel Sambuc /*
251f7cf2976SLionel Sambuc  * Turn off line numbers because the user has interrupted
252f7cf2976SLionel Sambuc  * a lengthy line number calculation.
253f7cf2976SLionel Sambuc  */
254f7cf2976SLionel Sambuc 	static void
abort_long()255f7cf2976SLionel Sambuc abort_long()
256f7cf2976SLionel Sambuc {
257f7cf2976SLionel Sambuc 	if (linenums == OPT_ONPLUS)
258f7cf2976SLionel Sambuc 		/*
259f7cf2976SLionel Sambuc 		 * We were displaying line numbers, so need to repaint.
260f7cf2976SLionel Sambuc 		 */
261f7cf2976SLionel Sambuc 		screen_trashed = 1;
262f7cf2976SLionel Sambuc 	linenums = 0;
263f7cf2976SLionel Sambuc 	error("Line numbers turned off", NULL_PARG);
264f7cf2976SLionel Sambuc }
265f7cf2976SLionel Sambuc 
266f7cf2976SLionel Sambuc /*
267f7cf2976SLionel Sambuc  * Find the line number associated with a given position.
268f7cf2976SLionel Sambuc  * Return 0 if we can't figure it out.
269f7cf2976SLionel Sambuc  */
270f7cf2976SLionel Sambuc 	public LINENUM
find_linenum(pos)271f7cf2976SLionel Sambuc find_linenum(pos)
272f7cf2976SLionel Sambuc 	POSITION pos;
273f7cf2976SLionel Sambuc {
274f7cf2976SLionel Sambuc 	register struct linenum_info *p;
275f7cf2976SLionel Sambuc 	register LINENUM linenum;
276f7cf2976SLionel Sambuc 	POSITION cpos;
277f7cf2976SLionel Sambuc 
278f7cf2976SLionel Sambuc 	if (!linenums)
279f7cf2976SLionel Sambuc 		/*
280f7cf2976SLionel Sambuc 		 * We're not using line numbers.
281f7cf2976SLionel Sambuc 		 */
282f7cf2976SLionel Sambuc 		return (0);
283f7cf2976SLionel Sambuc 	if (pos == NULL_POSITION)
284f7cf2976SLionel Sambuc 		/*
285f7cf2976SLionel Sambuc 		 * Caller doesn't know what he's talking about.
286f7cf2976SLionel Sambuc 		 */
287f7cf2976SLionel Sambuc 		return (0);
288f7cf2976SLionel Sambuc 	if (pos <= ch_zero())
289f7cf2976SLionel Sambuc 		/*
290f7cf2976SLionel Sambuc 		 * Beginning of file is always line number 1.
291f7cf2976SLionel Sambuc 		 */
292f7cf2976SLionel Sambuc 		return (1);
293f7cf2976SLionel Sambuc 
294f7cf2976SLionel Sambuc 	/*
295f7cf2976SLionel Sambuc 	 * Find the entry nearest to the position we want.
296f7cf2976SLionel Sambuc 	 */
297f7cf2976SLionel Sambuc 	for (p = anchor.next;  p != &anchor && p->pos < pos;  p = p->next)
298f7cf2976SLionel Sambuc 		continue;
299f7cf2976SLionel Sambuc 	if (p->pos == pos)
300f7cf2976SLionel Sambuc 		/* Found it exactly. */
301f7cf2976SLionel Sambuc 		return (p->line);
302f7cf2976SLionel Sambuc 
303f7cf2976SLionel Sambuc 	/*
304f7cf2976SLionel Sambuc 	 * This is the (possibly) time-consuming part.
305f7cf2976SLionel Sambuc 	 * We start at the line we just found and start
306f7cf2976SLionel Sambuc 	 * reading the file forward or backward till we
307f7cf2976SLionel Sambuc 	 * get to the place we want.
308f7cf2976SLionel Sambuc 	 *
309f7cf2976SLionel Sambuc 	 * First decide whether we should go forward from the
310f7cf2976SLionel Sambuc 	 * previous one or backwards from the next one.
311f7cf2976SLionel Sambuc 	 * The decision is based on which way involves
312f7cf2976SLionel Sambuc 	 * traversing fewer bytes in the file.
313f7cf2976SLionel Sambuc 	 */
314f7cf2976SLionel Sambuc #if HAVE_TIME
315f7cf2976SLionel Sambuc 	startime = get_time();
316f7cf2976SLionel Sambuc #endif
317f7cf2976SLionel Sambuc 	if (p == &anchor || pos - p->prev->pos < p->pos - pos)
318f7cf2976SLionel Sambuc 	{
319f7cf2976SLionel Sambuc 		/*
320f7cf2976SLionel Sambuc 		 * Go forward.
321f7cf2976SLionel Sambuc 		 */
322f7cf2976SLionel Sambuc 		p = p->prev;
323f7cf2976SLionel Sambuc 		if (ch_seek(p->pos))
324f7cf2976SLionel Sambuc 			return (0);
325f7cf2976SLionel Sambuc 		loopcount = 0;
326f7cf2976SLionel Sambuc 		for (linenum = p->line, cpos = p->pos;  cpos < pos;  linenum++)
327f7cf2976SLionel Sambuc 		{
328f7cf2976SLionel Sambuc 			/*
329f7cf2976SLionel Sambuc 			 * Allow a signal to abort this loop.
330f7cf2976SLionel Sambuc 			 */
331f7cf2976SLionel Sambuc 			cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
332f7cf2976SLionel Sambuc 			if (ABORT_SIGS()) {
333f7cf2976SLionel Sambuc 				abort_long();
334f7cf2976SLionel Sambuc 				return (0);
335f7cf2976SLionel Sambuc 			}
336f7cf2976SLionel Sambuc 			if (cpos == NULL_POSITION)
337f7cf2976SLionel Sambuc 				return (0);
338f7cf2976SLionel Sambuc 			longish();
339f7cf2976SLionel Sambuc 		}
340f7cf2976SLionel Sambuc 		/*
341f7cf2976SLionel Sambuc 		 * We might as well cache it.
342f7cf2976SLionel Sambuc 		 */
343f7cf2976SLionel Sambuc 		add_lnum(linenum, cpos);
344f7cf2976SLionel Sambuc 		/*
345f7cf2976SLionel Sambuc 		 * If the given position is not at the start of a line,
346f7cf2976SLionel Sambuc 		 * make sure we return the correct line number.
347f7cf2976SLionel Sambuc 		 */
348f7cf2976SLionel Sambuc 		if (cpos > pos)
349f7cf2976SLionel Sambuc 			linenum--;
350f7cf2976SLionel Sambuc 	} else
351f7cf2976SLionel Sambuc 	{
352f7cf2976SLionel Sambuc 		/*
353f7cf2976SLionel Sambuc 		 * Go backward.
354f7cf2976SLionel Sambuc 		 */
355f7cf2976SLionel Sambuc 		if (ch_seek(p->pos))
356f7cf2976SLionel Sambuc 			return (0);
357f7cf2976SLionel Sambuc 		loopcount = 0;
358f7cf2976SLionel Sambuc 		for (linenum = p->line, cpos = p->pos;  cpos > pos;  linenum--)
359f7cf2976SLionel Sambuc 		{
360f7cf2976SLionel Sambuc 			/*
361f7cf2976SLionel Sambuc 			 * Allow a signal to abort this loop.
362f7cf2976SLionel Sambuc 			 */
363f7cf2976SLionel Sambuc 			cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
364f7cf2976SLionel Sambuc 			if (ABORT_SIGS()) {
365f7cf2976SLionel Sambuc 				abort_long();
366f7cf2976SLionel Sambuc 				return (0);
367f7cf2976SLionel Sambuc 			}
368f7cf2976SLionel Sambuc 			if (cpos == NULL_POSITION)
369f7cf2976SLionel Sambuc 				return (0);
370f7cf2976SLionel Sambuc 			longish();
371f7cf2976SLionel Sambuc 		}
372f7cf2976SLionel Sambuc 		/*
373f7cf2976SLionel Sambuc 		 * We might as well cache it.
374f7cf2976SLionel Sambuc 		 */
375f7cf2976SLionel Sambuc 		add_lnum(linenum, cpos);
376f7cf2976SLionel Sambuc 	}
377f7cf2976SLionel Sambuc 
378f7cf2976SLionel Sambuc 	return (linenum);
379f7cf2976SLionel Sambuc }
380f7cf2976SLionel Sambuc 
381f7cf2976SLionel Sambuc /*
382f7cf2976SLionel Sambuc  * Find the position of a given line number.
383f7cf2976SLionel Sambuc  * Return NULL_POSITION if we can't figure it out.
384f7cf2976SLionel Sambuc  */
385f7cf2976SLionel Sambuc 	public POSITION
find_pos(linenum)386f7cf2976SLionel Sambuc find_pos(linenum)
387f7cf2976SLionel Sambuc 	LINENUM linenum;
388f7cf2976SLionel Sambuc {
389f7cf2976SLionel Sambuc 	register struct linenum_info *p;
390f7cf2976SLionel Sambuc 	POSITION cpos;
391f7cf2976SLionel Sambuc 	LINENUM clinenum;
392f7cf2976SLionel Sambuc 
393f7cf2976SLionel Sambuc 	if (linenum <= 1)
394f7cf2976SLionel Sambuc 		/*
395f7cf2976SLionel Sambuc 		 * Line number 1 is beginning of file.
396f7cf2976SLionel Sambuc 		 */
397f7cf2976SLionel Sambuc 		return (ch_zero());
398f7cf2976SLionel Sambuc 
399f7cf2976SLionel Sambuc 	/*
400f7cf2976SLionel Sambuc 	 * Find the entry nearest to the line number we want.
401f7cf2976SLionel Sambuc 	 */
402f7cf2976SLionel Sambuc 	for (p = anchor.next;  p != &anchor && p->line < linenum;  p = p->next)
403f7cf2976SLionel Sambuc 		continue;
404f7cf2976SLionel Sambuc 	if (p->line == linenum)
405f7cf2976SLionel Sambuc 		/* Found it exactly. */
406f7cf2976SLionel Sambuc 		return (p->pos);
407f7cf2976SLionel Sambuc 
408f7cf2976SLionel Sambuc 	if (p == &anchor || linenum - p->prev->line < p->line - linenum)
409f7cf2976SLionel Sambuc 	{
410f7cf2976SLionel Sambuc 		/*
411f7cf2976SLionel Sambuc 		 * Go forward.
412f7cf2976SLionel Sambuc 		 */
413f7cf2976SLionel Sambuc 		p = p->prev;
414f7cf2976SLionel Sambuc 		if (ch_seek(p->pos))
415f7cf2976SLionel Sambuc 			return (NULL_POSITION);
416f7cf2976SLionel Sambuc 		for (clinenum = p->line, cpos = p->pos;  clinenum < linenum;  clinenum++)
417f7cf2976SLionel Sambuc 		{
418f7cf2976SLionel Sambuc 			/*
419f7cf2976SLionel Sambuc 			 * Allow a signal to abort this loop.
420f7cf2976SLionel Sambuc 			 */
421f7cf2976SLionel Sambuc 			cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
422f7cf2976SLionel Sambuc 			if (ABORT_SIGS())
423f7cf2976SLionel Sambuc 				return (NULL_POSITION);
424f7cf2976SLionel Sambuc 			if (cpos == NULL_POSITION)
425f7cf2976SLionel Sambuc 				return (NULL_POSITION);
426f7cf2976SLionel Sambuc 		}
427f7cf2976SLionel Sambuc 	} else
428f7cf2976SLionel Sambuc 	{
429f7cf2976SLionel Sambuc 		/*
430f7cf2976SLionel Sambuc 		 * Go backward.
431f7cf2976SLionel Sambuc 		 */
432f7cf2976SLionel Sambuc 		if (ch_seek(p->pos))
433f7cf2976SLionel Sambuc 			return (NULL_POSITION);
434f7cf2976SLionel Sambuc 		for (clinenum = p->line, cpos = p->pos;  clinenum > linenum;  clinenum--)
435f7cf2976SLionel Sambuc 		{
436f7cf2976SLionel Sambuc 			/*
437f7cf2976SLionel Sambuc 			 * Allow a signal to abort this loop.
438f7cf2976SLionel Sambuc 			 */
439f7cf2976SLionel Sambuc 			cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
440f7cf2976SLionel Sambuc 			if (ABORT_SIGS())
441f7cf2976SLionel Sambuc 				return (NULL_POSITION);
442f7cf2976SLionel Sambuc 			if (cpos == NULL_POSITION)
443f7cf2976SLionel Sambuc 				return (NULL_POSITION);
444f7cf2976SLionel Sambuc 		}
445f7cf2976SLionel Sambuc 	}
446f7cf2976SLionel Sambuc 	/*
447f7cf2976SLionel Sambuc 	 * We might as well cache it.
448f7cf2976SLionel Sambuc 	 */
449f7cf2976SLionel Sambuc 	add_lnum(clinenum, cpos);
450f7cf2976SLionel Sambuc 	return (cpos);
451f7cf2976SLionel Sambuc }
452f7cf2976SLionel Sambuc 
453f7cf2976SLionel Sambuc /*
454f7cf2976SLionel Sambuc  * Return the line number of the "current" line.
455f7cf2976SLionel Sambuc  * The argument "where" tells which line is to be considered
456f7cf2976SLionel Sambuc  * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
457f7cf2976SLionel Sambuc  */
458f7cf2976SLionel Sambuc 	public LINENUM
currline(where)459f7cf2976SLionel Sambuc currline(where)
460f7cf2976SLionel Sambuc 	int where;
461f7cf2976SLionel Sambuc {
462f7cf2976SLionel Sambuc 	POSITION pos;
463f7cf2976SLionel Sambuc 	POSITION len;
464f7cf2976SLionel Sambuc 	LINENUM linenum;
465f7cf2976SLionel Sambuc 
466f7cf2976SLionel Sambuc 	pos = position(where);
467f7cf2976SLionel Sambuc 	len = ch_length();
468f7cf2976SLionel Sambuc 	while (pos == NULL_POSITION && where >= 0 && where < sc_height)
469f7cf2976SLionel Sambuc 		pos = position(++where);
470f7cf2976SLionel Sambuc 	if (pos == NULL_POSITION)
471f7cf2976SLionel Sambuc 		pos = len;
472f7cf2976SLionel Sambuc 	linenum = find_linenum(pos);
473f7cf2976SLionel Sambuc 	if (pos == len)
474f7cf2976SLionel Sambuc 		linenum--;
475f7cf2976SLionel Sambuc 	return (linenum);
476f7cf2976SLionel Sambuc }
477